From e2bbf175a2184bd76f6c54ccf8456babeb1a46fc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 9 Apr 2024 15:16:35 +0200 Subject: Adding upstream version 9.1. Signed-off-by: Daniel Baumann --- ospfd/.gitignore | 2 + ospfd/ChangeLog.opaque.txt | 221 + ospfd/Makefile | 10 + ospfd/OSPF-ALIGNMENT.txt | 117 + ospfd/ospf_abr.c | 2178 ++++++ ospfd/ospf_abr.h | 96 + ospfd/ospf_api.c | 680 ++ ospfd/ospf_api.h | 348 + ospfd/ospf_apiserver.c | 2723 ++++++++ ospfd/ospf_apiserver.h | 205 + ospfd/ospf_asbr.c | 1224 ++++ ospfd/ospf_asbr.h | 157 + ospfd/ospf_ase.c | 789 +++ ospfd/ospf_ase.h | 25 + ospfd/ospf_auth.c | 716 ++ ospfd/ospf_auth.h | 20 + ospfd/ospf_bfd.c | 323 + ospfd/ospf_bfd.h | 41 + ospfd/ospf_dump.c | 2072 ++++++ ospfd/ospf_dump.h | 153 + ospfd/ospf_dump_api.c | 122 + ospfd/ospf_dump_api.h | 29 + ospfd/ospf_errors.c | 181 + ospfd/ospf_errors.h | 43 + ospfd/ospf_ext.c | 1952 ++++++ ospfd/ospf_ext.h | 184 + ospfd/ospf_flood.c | 1262 ++++ ospfd/ospf_flood.h | 59 + ospfd/ospf_gr.c | 875 +++ ospfd/ospf_gr.h | 181 + ospfd/ospf_gr_helper.c | 1125 ++++ ospfd/ospf_ia.c | 668 ++ ospfd/ospf_ia.h | 29 + ospfd/ospf_interface.c | 1571 +++++ ospfd/ospf_interface.h | 380 ++ ospfd/ospf_ism.c | 595 ++ ospfd/ospf_ism.h | 84 + ospfd/ospf_ldp_sync.c | 1064 +++ ospfd/ospf_ldp_sync.h | 43 + ospfd/ospf_lsa.c | 4271 ++++++++++++ ospfd/ospf_lsa.h | 375 ++ ospfd/ospf_lsdb.c | 284 + ospfd/ospf_lsdb.h | 67 + ospfd/ospf_main.c | 255 + ospfd/ospf_memory.c | 47 + ospfd/ospf_memory.h | 48 + ospfd/ospf_neighbor.c | 499 ++ ospfd/ospf_neighbor.h | 105 + ospfd/ospf_network.c | 323 + ospfd/ospf_network.h | 32 + ospfd/ospf_nsm.c | 875 +++ ospfd/ospf_nsm.h | 65 + ospfd/ospf_opaque.c | 2166 ++++++ ospfd/ospf_opaque.h | 163 + ospfd/ospf_packet.c | 4023 +++++++++++ ospfd/ospf_packet.h | 153 + ospfd/ospf_ri.c | 2123 ++++++ ospfd/ospf_ri.h | 240 + ospfd/ospf_route.c | 1121 ++++ ospfd/ospf_route.h | 164 + ospfd/ospf_routemap.c | 745 ++ ospfd/ospf_routemap_nb.c | 28 + ospfd/ospf_routemap_nb.h | 24 + ospfd/ospf_routemap_nb_config.c | 52 + ospfd/ospf_snmp.c | 2553 +++++++ ospfd/ospf_spf.c | 2073 ++++++ ospfd/ospf_spf.h | 92 + ospfd/ospf_sr.c | 2961 ++++++++ ospfd/ospf_sr.h | 364 + ospfd/ospf_te.c | 4423 ++++++++++++ ospfd/ospf_te.h | 421 ++ ospfd/ospf_ti_lfa.c | 1108 +++ ospfd/ospf_ti_lfa.h | 26 + ospfd/ospf_vty.c | 13798 ++++++++++++++++++++++++++++++++++++++ ospfd/ospf_vty.h | 46 + ospfd/ospf_zebra.c | 2224 ++++++ ospfd/ospf_zebra.h | 99 + ospfd/ospfd.c | 2423 +++++++ ospfd/ospfd.h | 820 +++ ospfd/subdir.am | 127 + 80 files changed, 74353 insertions(+) create mode 100644 ospfd/.gitignore create mode 100644 ospfd/ChangeLog.opaque.txt create mode 100644 ospfd/Makefile create mode 100644 ospfd/OSPF-ALIGNMENT.txt create mode 100644 ospfd/ospf_abr.c create mode 100644 ospfd/ospf_abr.h create mode 100644 ospfd/ospf_api.c create mode 100644 ospfd/ospf_api.h create mode 100644 ospfd/ospf_apiserver.c create mode 100644 ospfd/ospf_apiserver.h create mode 100644 ospfd/ospf_asbr.c create mode 100644 ospfd/ospf_asbr.h create mode 100644 ospfd/ospf_ase.c create mode 100644 ospfd/ospf_ase.h create mode 100644 ospfd/ospf_auth.c create mode 100644 ospfd/ospf_auth.h create mode 100644 ospfd/ospf_bfd.c create mode 100644 ospfd/ospf_bfd.h create mode 100644 ospfd/ospf_dump.c create mode 100644 ospfd/ospf_dump.h create mode 100644 ospfd/ospf_dump_api.c create mode 100644 ospfd/ospf_dump_api.h create mode 100644 ospfd/ospf_errors.c create mode 100644 ospfd/ospf_errors.h create mode 100644 ospfd/ospf_ext.c create mode 100644 ospfd/ospf_ext.h create mode 100644 ospfd/ospf_flood.c create mode 100644 ospfd/ospf_flood.h create mode 100644 ospfd/ospf_gr.c create mode 100644 ospfd/ospf_gr.h create mode 100644 ospfd/ospf_gr_helper.c create mode 100644 ospfd/ospf_ia.c create mode 100644 ospfd/ospf_ia.h create mode 100644 ospfd/ospf_interface.c create mode 100644 ospfd/ospf_interface.h create mode 100644 ospfd/ospf_ism.c create mode 100644 ospfd/ospf_ism.h create mode 100644 ospfd/ospf_ldp_sync.c create mode 100644 ospfd/ospf_ldp_sync.h create mode 100644 ospfd/ospf_lsa.c create mode 100644 ospfd/ospf_lsa.h create mode 100644 ospfd/ospf_lsdb.c create mode 100644 ospfd/ospf_lsdb.h create mode 100644 ospfd/ospf_main.c create mode 100644 ospfd/ospf_memory.c create mode 100644 ospfd/ospf_memory.h create mode 100644 ospfd/ospf_neighbor.c create mode 100644 ospfd/ospf_neighbor.h create mode 100644 ospfd/ospf_network.c create mode 100644 ospfd/ospf_network.h create mode 100644 ospfd/ospf_nsm.c create mode 100644 ospfd/ospf_nsm.h create mode 100644 ospfd/ospf_opaque.c create mode 100644 ospfd/ospf_opaque.h create mode 100644 ospfd/ospf_packet.c create mode 100644 ospfd/ospf_packet.h create mode 100644 ospfd/ospf_ri.c create mode 100644 ospfd/ospf_ri.h create mode 100644 ospfd/ospf_route.c create mode 100644 ospfd/ospf_route.h create mode 100644 ospfd/ospf_routemap.c create mode 100644 ospfd/ospf_routemap_nb.c create mode 100644 ospfd/ospf_routemap_nb.h create mode 100644 ospfd/ospf_routemap_nb_config.c create mode 100644 ospfd/ospf_snmp.c create mode 100644 ospfd/ospf_spf.c create mode 100644 ospfd/ospf_spf.h create mode 100644 ospfd/ospf_sr.c create mode 100644 ospfd/ospf_sr.h create mode 100644 ospfd/ospf_te.c create mode 100644 ospfd/ospf_te.h create mode 100644 ospfd/ospf_ti_lfa.c create mode 100644 ospfd/ospf_ti_lfa.h create mode 100644 ospfd/ospf_vty.c create mode 100644 ospfd/ospf_vty.h create mode 100644 ospfd/ospf_zebra.c create mode 100644 ospfd/ospf_zebra.h create mode 100644 ospfd/ospfd.c create mode 100644 ospfd/ospfd.h create mode 100644 ospfd/subdir.am (limited to 'ospfd') diff --git a/ospfd/.gitignore b/ospfd/.gitignore new file mode 100644 index 0000000..fc65db3 --- /dev/null +++ b/ospfd/.gitignore @@ -0,0 +1,2 @@ +ospfd +ospfd.conf diff --git a/ospfd/ChangeLog.opaque.txt b/ospfd/ChangeLog.opaque.txt new file mode 100644 index 0000000..782e332 --- /dev/null +++ b/ospfd/ChangeLog.opaque.txt @@ -0,0 +1,221 @@ +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +Changes 2002.12.20 + +1. Bug fixes + + 1.1 When an opaque LSA is being removed from (or added to) the LSDB, + it does not mean a change in network topology. Therefore, SPF + recalculation should not be triggered in that case. + There was an assertion failure problem "assert (rn && rn->info)" + inside the function "ospf_ase_incremental_update()", because + the upper function "ospf_lsa_maxage_walker_remover()" called it + when a type-11 opaque LSA is removed due to MaxAge. + + 1.2 Type-9 LSA is defined to have "link-local" flooding scope. + In the Database exchange procedure with a new neighbor, a type-9 + LSA was added in the database summary of a DD message, even if + the link is different from the one that have bound to. + +2. Feature enhancements + + 2.1 Though a "wildcard" concept to handle type-9/10/11 LSAs altogether + has introduced about a year ago, it was only a symbol definition + and actual handling mechanism was not implemented. Now it works. + +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +Changes 2002.7.8 + +1. Bug fixes + + 1.1 When "ospf_delete_opaque_functab()" is called, internal structure + "oipt" remain unfreed. If register/delete functab is repeated, + illegal memory access happens due to this "oipt". + + 1.2 In "free_opaque_info_per_id()", there was a crucial typo which + ignores a condition test. + + "if (oipi->lsa != NULL);" <-- semicolon! + +2. Feature enhancements + + None. + +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +Changes 2001.12.03 + +1. Bug fixes + + 1.1 Though a new member "oi" has added to "struct ospf_lsa" to control + flooding scope of type-9 Opaque-LSAs, the value was always NULL + because no one set it. + + 1.2 In the function "show_ip_ospf_database_summary()" and "show_lsa_ + detail_adv_router()", VTY output for type-11 Opaque-LSAs did not + work properly. + + 1.3 URL for the opaque-type assignment reference has changed. + + 1.4 In the file "ospf_mpls_te.c", printf formats have changed to + avoid compiler warning messages; "%lu" -> "%u", "%lx" -> "%x". + Note that this hack depends on OS, compiler and their versions. + + 1.5 One of attached documentation "opaque_lsa.txt" has changed to + reflect the latest coding. + +2. Feature enhancements + + 2.1 Knowing that it is an ugly hack, an "officially unallocated" + opaque-type value 0 has newly introduced as a "wildcard", + which matches to all opaque-type. + This value must not be flooded to the network, of course. + + 2.2 The Opaque-core module makes use of newly introduced hooks to + dispatch every LSDB change (LSA installation and deletion) to + preregistered opaque users. + Therefore, by providing appropriate callback functions as new + parameters of "ospf_register_opaque_functab()", an opaque user + can refer to every LSA instance to be installed into, or to be + deleted from, the LSDB. + +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +Changes 2001.10.31 + +1. Bug fixes + + 1.1 Since each LSA has their own lifetime, they will remain in a + routing domain (being stored in LSDB of each router), until their + age naturally reach to MaxAge or explicitly being flushed by the + originated router. Therefore, if a router restarted with a short + downtime, it is possible that previously flooded self-originated + LSAs might received if the NSM status is not less than Exchange. + + There were some problems in the way of handling self-originated + Opaque-LSAs if they are contained in a received LSUpd message, + but not installed to the local LSDB yet. + Regardless of some conditions to start originating Opaque-LSAs + (there should be at least one opaque-capable full-state neighbor), + the function "ospf_flood()" will be called to flood and install + this brand-new looking LSA. + As the result, when the NSM of an opaque-capable neighbor gets + full, internal state inconsistency happens; a user of Opaque-LSA + such as MPLS-TE can refer to self-originated LSAs in the local + LSDB, but cannot modify their contents... + + Above problems have fixed with a policy "flush it from the whole + routing domain and keep silent until the flushing completed". + By using this sweeping technique, we can be free from confusion + caused by self-originated LSAs received via network. + + 1.2 The function "ospf_opaque_type_name()" contained massive ifdefs + corresponding to each "opaque-type". + These unnecessary ifdefs are removed completely. + + 1.3 In the function "ospf_delete_opaque_functab()", there was an + improper loop control that causes illegal memory access. + Original coding was "next = nextnode (node)". + + 1.4 The function "ospf_mpls_te_ism_change()" could not handle the + case when the ISM changes from Waiting to DR/BDR/Other. + So, there was a case that even if one of an ISM become + operational and MPLS-TE module has started, the corresponding + Opaque-LSA cannot be originated. + + 1.5 The function "ospf_opaque_lsa_reoriginate_schedule()" did not + allow to be called multiple times, simply because handling + module for the given "lsa-type & opaque-type" already exists. + But this assumption seems to be wrong. + Change the policy to allow this function to be called multiple + times and let the caller to decide what should do when the + corresponding callback function "(* functab->lsa_originator)()" + is called. + +2. Feature enhancements + + 2.1 The global bitmap "opaque" has introduced instead of former flag + "OpaqueCapable", to store complex conditions to handle Opaque-LSAs. + + 2.2 The MPLS-TE module now referes to "draft-katz-yeung-ospf-traffic + -06.txt", no significant changes with 05 version, though. + +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +Changes 2001.08.03 + +1. Bug fixes + + 1.1 Even if the ospfd started with opaque capability enabled, when + the ospfd receives an unknown opaque-type (unregistered by the + function "ospf_register_opaque_functab()" beforehand), the LSA + was discarded. As the result, only the opaque-LSAs that have + commonly registered by opaque-capable ospf routers can be + flooded in a routing domain. + + This behavior has fixed so that arbitrary opaque-type LSAs can + be flooded among opaque-capable ospf routers. + If the ospfd has opaque-LSA capability but disabled at runtime, + received opaque-LSAs can be accepted and registered to LSDB as + is, but not be flooded to the network; those opaque LSAs will + remain in LSDB until explicitly flushed by incoming LSUpd + messages with MaxAge, or their age naturally reaches to MaxAge. + + 1.2 The function "ospf_register_opaque_functab()" did not check + if the entry corresponding to the given "lsa-type, opaque-type" + combination already exists or not. + This problem has fixed not to allow multiple registration. + + 1.3 Since type-11 (AS external) LSAs will be flooded beyond areas, + there is little relationship between "struct lsa" and "struct + area". More specifically, the pointer address "lsa->area" can + be NULL if the lsa-type is 11, thus an illegal memory access + will happen. This problem has fixed. + + 1.4 When self-originated opaque-LSAs are received via network and + if the corresponding opaque-type functions are not available + (they have already deleted) at that time, those LSAs were + dropped due to "unknown opaque-type" error. + After the problem 1.1 has fixed, those "self-originated" LSAs + were registered to LSDB and then flooded to the network, even + if the processing functions did not exist... + + After all, this problem has fixed so that those LSAs should + explicitly be flushed from the routing domain immediately, if + the processing functions cannot find at that time. + + 1.5 Some typo have fixed. + + --- EXAMPLE --- + static int + opaque_lsa_originate_callback (list funclist, void *lsa_type_dependent) + ^^^^^ + --- EXAMPLE --- + +2. Feature enhancements + + 2.1 According to the description of rfc2328 in section 10.8, any + change in the router's optional capabilities should trigger + the option re-negotiation procedures with neighbors. + + --- EXCERPT --- + If for some reason the router's optional + capabilities change, the Database Exchange procedure should be + restarted by reverting to neighbor state ExStart. + --- EXCERPT --- + + For the opaque-capability changes, this feature has implemented. + More specifically, if "ospf opaque-lsa" or "no ospf opaque-lsa" + VTY command is given at runtime, all self-originated LSAs will + be flushed immediately and then all neighbor status will be + forced to ExStart by generating SeqNumberMismatch events. + + 2.1 When we change opaque-capability dynamically (ON -> OFF -> ON), + there was no trigger at "OFF->ON" timing to reactivate opaque + LSA handling modules (such as MPLS-TE) that have once forcibly + stopped at "ON->OFF" timing. + Now this dynamic reactivation feature has added. + + 2.2 The MPLS-TE module now referes to "draft-katz-yeung-ospf-traffic + -05.txt", no significant changes with 04 version, though. + +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +Changes 2001.03.28 + + Initial release of Opaque-LSA/MPLS-TE extensions for the zebra/ospfd. diff --git a/ospfd/Makefile b/ospfd/Makefile new file mode 100644 index 0000000..e4fab30 --- /dev/null +++ b/ospfd/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. ospfd/ospfd +%: ALWAYS + @$(MAKE) -s -C .. ospfd/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/ospfd/OSPF-ALIGNMENT.txt b/ospfd/OSPF-ALIGNMENT.txt new file mode 100644 index 0000000..905bd22 --- /dev/null +++ b/ospfd/OSPF-ALIGNMENT.txt @@ -0,0 +1,117 @@ +Greg Troxel +2004-11-17 + +The OSPF specification (RFC2328) and the OSPF Opaque LSA specification +(RFC2370) are ambiguous about LSAs whose data section is not an +integral multiple of 4 octets. This note examines the issue and +proposes clarifications to ensure interoperability. + +RFC2328 does not specify that LSA lengths be a multiple of 4. +It does not require that LSAs in update packets be aligned. +However, all structures defined by RFC2328 are multiples of 4, and +thus update packets with those structures must be aligned. +LSA length is defined in Appendix A.4 as + + length + The length in bytes of the LSA. This includes the 20 byte LSA + header. + +RFC2370 defines Opaque LSAs, which are intended to contain arbitrary +data: + + This memo defines enhancements to the OSPF protocol to support a new + class of link-state advertisements (LSA) called Opaque LSAs. Opaque + LSAs provide a generalized mechanism to allow for the future + extensibility of OSPF. Opaque LSAs consist of a standard LSA header + followed by application-specific information. The information field + may be used directly by OSPF or by other applications. Standard OSPF + link-state database flooding mechanisms are used to distribute Opaque + LSAs to all or some limited portion of the OSPF topology. + + +Later, 2370 says: + + Opaque LSAs contain some number of octets (of application-specific + data) padded to 32-bit alignment. + +This can be interpreted in several ways: + +A) The payload may be any number of octets, and the length field +reflects the payload length (e.g. length 23 for 3 octets of payload), +but there are padding octets following the LSA in packets, so that the +next LSA starts on a 4-octet boundary. (This approach is common in +the BSD user/kernel interface.) + +B) The payload must be a multiple of 4 octets, so that the length is a +multiple of 4 octets. This corresponds to an implementation that +treats an Opaque LSA publish request that is not a multiple of 4 +octets as an error. + +C) The payload can be any number of octets, but padding is added and +included in the length field. This interpretation corresponds to an +OSPF implementation that accepts a publish request for an Opaque LSA +that is not a multiple of 4 octets. This interpretation is +nonsensical, because it claims to represent arbitrary lengths, but +does not actually do so --- the receiver cannot distinguish padding +from supplied data. + +D) Accept according to A, and transmit according to B. + +Option A arguably violates RFC 2328, which doesn't say anything about +adding padding (A.3.5 shows a diagram of adjacent LSAs which are shown +as all multiples of 4). This option is thus likely to lead to a lack +of interoperability. + +Option B restricts what data can be represented as an Opaque LSA, but +probably not in a serious way. It is likely to lead to +interoperability in that the complex case of non-multiple-of-4 lengths +will not arise. + +However, an implementation that follows A and emits an LSA with +payload length not a multiple of 4 will not interoperate with an +Option B implementation. + +Given that all known and documented uses of Opaque LSAs seem to be +multiples of 4 octets, we choose Option B as the clarification. + +CLARIFYING TEXT + +In RFC2328: + +In section A.4, add a second sentence about length: + + length + The length in bytes of the LSA. This includes the 20 byte LSA + header. The length must be an integral multiple of 4 bytes. + +Add to the list in Section 13: + + Verify that the length of the LSA is a multiple of 4 bytes. If + not, discard the entire Link State Update Packet. + +In RFC2380: + +Change text: + + Opaque LSAs contain some number of octets (of application-specific + data) padded to 32-bit alignment. + +to: + + Opaque LSAs contain some a number of octets (of + application-specific data). The number of octets must be a + multiple of four. + + +HOW THIS ISSUE AROSE + +At BBN, we use Opaque LSAs to exchange data among routers; the format +of the data is naturally aligned to 4 bytes, and thus does not raise +this issue. We created a test program to publish Opaque data via IPC +to the OSPF daemon (quagga), and this program accepts strings on the +command line to publish. We then used this test program to publish +software version strings. Quagga's ospfd then crashed on a +NetBSD/sparc64 machine with an alignment fault, because the odd-length +LSAs were marshalled into a link-state update packet with no padding. +While this behavior was a clear violation of RFC2380, it was not clear +how to remedy the problem. diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c new file mode 100644 index 0000000..28d5268 --- /dev/null +++ b/ospfd/ospf_abr.c @@ -0,0 +1,2178 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF ABR functions. + * Copyright (C) 1999, 2000 Alex Zinin, Toshiaki Takada + */ + + +#include + +#include "frrevent.h" +#include "memory.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "vty.h" +#include "filter.h" +#include "plist.h" +#include "log.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ia.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_errors.h" + +static struct ospf_area_range *ospf_area_range_new(struct prefix_ipv4 *p) +{ + struct ospf_area_range *range; + + range = XCALLOC(MTYPE_OSPF_AREA_RANGE, sizeof(struct ospf_area_range)); + range->addr = p->prefix; + range->masklen = p->prefixlen; + range->cost_config = OSPF_AREA_RANGE_COST_UNSPEC; + + return range; +} + +static void ospf_area_range_free(struct ospf_area_range *range) +{ + XFREE(MTYPE_OSPF_AREA_RANGE, range); +} + +static void ospf_area_range_add(struct ospf_area *area, + struct route_table *ranges, + struct ospf_area_range *range) +{ + struct route_node *rn; + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefixlen = range->masklen; + p.prefix = range->addr; + apply_mask_ipv4(&p); + + rn = route_node_get(ranges, (struct prefix *)&p); + if (rn->info) { + route_unlock_node(rn); + ospf_area_range_free(rn->info); + rn->info = range; + } else + rn->info = range; +} + +static void ospf_area_range_delete(struct ospf_area *area, + struct route_node *rn) +{ + struct ospf_area_range *range = rn->info; + bool nssa = CHECK_FLAG(range->flags, OSPF_AREA_RANGE_NSSA); + + if (ospf_area_range_active(range) && + CHECK_FLAG(range->flags, OSPF_AREA_RANGE_ADVERTISE)) + ospf_delete_discard_route(area->ospf, area->ospf->new_table, + (struct prefix_ipv4 *)&rn->p, nssa); + + ospf_area_range_free(range); + rn->info = NULL; + route_unlock_node(rn); + route_unlock_node(rn); +} + +struct ospf_area_range *ospf_area_range_lookup(struct ospf_area *area, + struct route_table *ranges, + struct prefix_ipv4 *p) +{ + struct route_node *rn; + + rn = route_node_lookup(ranges, (struct prefix *)p); + if (rn) { + route_unlock_node(rn); + return rn->info; + } + return NULL; +} + +struct ospf_area_range *ospf_area_range_lookup_next(struct ospf_area *area, + struct in_addr *range_net, + int first) +{ + struct route_node *rn; + struct prefix_ipv4 p; + struct ospf_area_range *find; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.prefix = *range_net; + apply_mask_ipv4(&p); + + if (first) + rn = route_top(area->ranges); + else { + rn = route_node_get(area->ranges, (struct prefix *)&p); + rn = route_next(rn); + } + + for (; rn; rn = route_next(rn)) + if (rn->info) + break; + + if (rn && rn->info) { + find = rn->info; + *range_net = rn->p.u.prefix4; + route_unlock_node(rn); + return find; + } + return NULL; +} + +static struct ospf_area_range *ospf_area_range_match(struct ospf_area *area, + struct route_table *ranges, + struct prefix_ipv4 *p) +{ + struct route_node *node; + + node = route_node_match(ranges, (struct prefix *)p); + if (node) { + route_unlock_node(node); + return node->info; + } + return NULL; +} + +struct ospf_area_range *ospf_area_range_match_any(struct ospf *ospf, + struct prefix_ipv4 *p) +{ + struct ospf_area_range *range; + struct ospf_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) + if ((range = ospf_area_range_match(area, area->ranges, p))) + return range; + + return NULL; +} + +int ospf_area_range_active(struct ospf_area_range *range) +{ + return range->specifics; +} + +static int ospf_area_actively_attached(struct ospf_area *area) +{ + return area->act_ints; +} + +int ospf_area_range_set(struct ospf *ospf, struct ospf_area *area, + struct route_table *ranges, struct prefix_ipv4 *p, + int advertise, bool nssa) +{ + struct ospf_area_range *range; + + range = ospf_area_range_lookup(area, ranges, p); + if (range != NULL) { + if (!CHECK_FLAG(advertise, OSPF_AREA_RANGE_ADVERTISE)) + range->cost_config = OSPF_AREA_RANGE_COST_UNSPEC; + if ((CHECK_FLAG(range->flags, OSPF_AREA_RANGE_ADVERTISE) + && !CHECK_FLAG(advertise, OSPF_AREA_RANGE_ADVERTISE)) + || (!CHECK_FLAG(range->flags, OSPF_AREA_RANGE_ADVERTISE) + && CHECK_FLAG(advertise, OSPF_AREA_RANGE_ADVERTISE))) + ospf_schedule_abr_task(ospf); + } else { + range = ospf_area_range_new(p); + ospf_area_range_add(area, ranges, range); + ospf_schedule_abr_task(ospf); + } + + if (CHECK_FLAG(advertise, OSPF_AREA_RANGE_ADVERTISE)) + SET_FLAG(range->flags, OSPF_AREA_RANGE_ADVERTISE); + else { + UNSET_FLAG(range->flags, OSPF_AREA_RANGE_ADVERTISE); + range->cost_config = OSPF_AREA_RANGE_COST_UNSPEC; + } + + if (nssa) + SET_FLAG(range->flags, OSPF_AREA_RANGE_NSSA); + + return 1; +} + +int ospf_area_range_cost_set(struct ospf *ospf, struct ospf_area *area, + struct route_table *ranges, struct prefix_ipv4 *p, + uint32_t cost) +{ + struct ospf_area_range *range; + + range = ospf_area_range_lookup(area, ranges, p); + if (range == NULL) + return 0; + + if (range->cost_config != cost) { + range->cost_config = cost; + if (ospf_area_range_active(range)) + ospf_schedule_abr_task(ospf); + } + + return 1; +} + +int ospf_area_range_unset(struct ospf *ospf, struct ospf_area *area, + struct route_table *ranges, struct prefix_ipv4 *p) +{ + struct route_node *rn; + + rn = route_node_lookup(ranges, (struct prefix *)p); + if (rn == NULL) + return 0; + + if (ospf_area_range_active(rn->info)) + ospf_schedule_abr_task(ospf); + + ospf_area_range_delete(area, rn); + + return 1; +} + +int ospf_area_range_substitute_set(struct ospf *ospf, struct ospf_area *area, + struct prefix_ipv4 *p, struct prefix_ipv4 *s) +{ + struct ospf_area_range *range; + + range = ospf_area_range_lookup(area, area->ranges, p); + + if (range != NULL) { + if (!CHECK_FLAG(range->flags, OSPF_AREA_RANGE_ADVERTISE) + || !CHECK_FLAG(range->flags, OSPF_AREA_RANGE_SUBSTITUTE)) + ospf_schedule_abr_task(ospf); + } else { + range = ospf_area_range_new(p); + ospf_area_range_add(area, area->ranges, range); + ospf_schedule_abr_task(ospf); + } + + SET_FLAG(range->flags, OSPF_AREA_RANGE_ADVERTISE); + SET_FLAG(range->flags, OSPF_AREA_RANGE_SUBSTITUTE); + range->subst_addr = s->prefix; + range->subst_masklen = s->prefixlen; + + return 1; +} + +int ospf_area_range_substitute_unset(struct ospf *ospf, struct ospf_area *area, + struct prefix_ipv4 *p) +{ + struct ospf_area_range *range; + + range = ospf_area_range_lookup(area, area->ranges, p); + if (range == NULL) + return 0; + + if (CHECK_FLAG(range->flags, OSPF_AREA_RANGE_SUBSTITUTE)) + if (ospf_area_range_active(range)) + ospf_schedule_abr_task(ospf); + + UNSET_FLAG(range->flags, OSPF_AREA_RANGE_SUBSTITUTE); + range->subst_addr.s_addr = INADDR_ANY; + range->subst_masklen = 0; + + return 1; +} + +int ospf_act_bb_connection(struct ospf *ospf) +{ + struct ospf_interface *oi; + struct listnode *node; + int full_nbrs = 0; + + if (ospf->backbone == NULL) + return 0; + + for (ALL_LIST_ELEMENTS_RO(ospf->backbone->oiflist, node, oi)) { + struct ospf_neighbor *nbr; + struct route_node *rn; + + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + nbr = rn->info; + if (!nbr) + continue; + + if (nbr->state == NSM_Full + || OSPF_GR_IS_ACTIVE_HELPER(nbr)) + full_nbrs++; + } + } + + return full_nbrs; +} + +/* Determine whether this router is elected translator or not for area */ +static int ospf_abr_nssa_am_elected(struct ospf_area *area) +{ + struct route_node *rn; + struct ospf_lsa *lsa; + struct router_lsa *rlsa; + struct in_addr *best = NULL; + + LSDB_LOOP (ROUTER_LSDB(area), rn, lsa) { + /* sanity checks */ + if (!lsa || (lsa->data->type != OSPF_ROUTER_LSA) + || IS_LSA_SELF(lsa)) + continue; + + rlsa = (struct router_lsa *)lsa->data; + + /* ignore non-ABR routers */ + if (!IS_ROUTER_LSA_BORDER(rlsa)) + continue; + + /* Router has Nt flag - always translate */ + if (IS_ROUTER_LSA_NT(rlsa)) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: router %pI4 asserts Nt", + __func__, &lsa->data->id); + return 0; + } + + if (best == NULL) + best = &lsa->data->id; + else if (IPV4_ADDR_CMP(&best->s_addr, &lsa->data->id.s_addr) + < 0) + best = &lsa->data->id; + } + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: best electable ABR is: %pI4", __func__, best); + + if (best == NULL) + return 1; + + if (IPV4_ADDR_CMP(&best->s_addr, &area->ospf->router_id.s_addr) < 0) + return 1; + else + return 0; +} + +/* Check NSSA ABR status + * assumes there are nssa areas + */ +void ospf_abr_nssa_check_status(struct ospf *ospf) +{ + struct ospf_area *area; + struct listnode *lnode, *nnode; + + for (ALL_LIST_ELEMENTS(ospf->areas, lnode, nnode, area)) { + uint8_t old_state = area->NSSATranslatorState; + + if (area->external_routing != OSPF_AREA_NSSA) + continue; + + if (IS_DEBUG_OSPF(nssa, NSSA)) + zlog_debug("%s: checking area %pI4", __func__, + &area->area_id); + + if (!IS_OSPF_ABR(area->ospf)) { + if (IS_DEBUG_OSPF(nssa, NSSA)) + zlog_debug("%s: not ABR", __func__); + area->NSSATranslatorState = + OSPF_NSSA_TRANSLATE_DISABLED; + } else { + switch (area->NSSATranslatorRole) { + case OSPF_NSSA_ROLE_NEVER: + /* We never Translate Type-7 LSA. */ + /* TODO: check previous state and flush? */ + if (IS_DEBUG_OSPF(nssa, NSSA)) + zlog_debug("%s: never translate", + __func__); + area->NSSATranslatorState = + OSPF_NSSA_TRANSLATE_DISABLED; + break; + + case OSPF_NSSA_ROLE_ALWAYS: + /* We always translate if we are an ABR + * TODO: originate new LSAs if state change? + * or let the nssa abr task take care of it? + */ + if (IS_DEBUG_OSPF(nssa, NSSA)) + zlog_debug("%s: translate always", + __func__); + area->NSSATranslatorState = + OSPF_NSSA_TRANSLATE_ENABLED; + break; + + case OSPF_NSSA_ROLE_CANDIDATE: + /* We are a candidate for Translation */ + if (ospf_abr_nssa_am_elected(area) > 0) { + area->NSSATranslatorState = + OSPF_NSSA_TRANSLATE_ENABLED; + if (IS_DEBUG_OSPF(nssa, NSSA)) + zlog_debug( + "%s: elected translator", + __func__); + } else { + area->NSSATranslatorState = + OSPF_NSSA_TRANSLATE_DISABLED; + if (IS_DEBUG_OSPF(nssa, NSSA)) + zlog_debug("%s: not elected", + __func__); + } + break; + } + } + /* 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 (old_state != area->NSSATranslatorState) { + if (old_state == OSPF_NSSA_TRANSLATE_DISABLED) + ospf_asbr_status_update(ospf, + ++ospf->redistribute); + else if (area->NSSATranslatorState + == OSPF_NSSA_TRANSLATE_DISABLED) + ospf_asbr_status_update(ospf, + --ospf->redistribute); + } + } +} + +/* Check area border router status. */ +void ospf_check_abr_status(struct ospf *ospf) +{ + struct ospf_area *area; + struct listnode *node, *nnode; + int bb_configured = 0; + int bb_act_attached = 0; + int areas_configured = 0; + int areas_act_attached = 0; + uint8_t new_flags = ospf->flags; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Start", __func__); + + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + if (listcount(area->oiflist)) { + areas_configured++; + + if (OSPF_IS_AREA_BACKBONE(area)) + bb_configured = 1; + } + + if (ospf_area_actively_attached(area)) { + areas_act_attached++; + + if (OSPF_IS_AREA_BACKBONE(area)) + bb_act_attached = 1; + } + } + + if (IS_DEBUG_OSPF_EVENT) { + zlog_debug("%s: looked through areas", __func__); + zlog_debug("%s: bb_configured: %d", __func__, bb_configured); + zlog_debug("%s: bb_act_attached: %d", __func__, + bb_act_attached); + zlog_debug("%s: areas_configured: %d", __func__, + areas_configured); + zlog_debug("%s: areas_act_attached: %d", __func__, + areas_act_attached); + } + + switch (ospf->abr_type) { + case OSPF_ABR_SHORTCUT: + case OSPF_ABR_STAND: + if (areas_act_attached > 1) + SET_FLAG(new_flags, OSPF_FLAG_ABR); + else + UNSET_FLAG(new_flags, OSPF_FLAG_ABR); + break; + + case OSPF_ABR_IBM: + if ((areas_act_attached > 1) && bb_configured) + SET_FLAG(new_flags, OSPF_FLAG_ABR); + else + UNSET_FLAG(new_flags, OSPF_FLAG_ABR); + break; + + case OSPF_ABR_CISCO: + if ((areas_configured > 1) && bb_act_attached) + SET_FLAG(new_flags, OSPF_FLAG_ABR); + else + UNSET_FLAG(new_flags, OSPF_FLAG_ABR); + break; + default: + break; + } + + if (new_flags != ospf->flags) { + ospf_spf_calculate_schedule(ospf, SPF_FLAG_ABR_STATUS_CHANGE); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: new router flags: %x", __func__, + new_flags); + ospf->flags = new_flags; + ospf_router_lsa_update(ospf); + } +} + +static void ospf_abr_update_aggregate(struct ospf_area_range *range, + uint32_t cost, struct ospf_area *area) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Start", __func__); + + if (CHECK_FLAG(area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED) + && (range->cost != OSPF_STUB_MAX_METRIC_SUMMARY_COST)) { + range->cost = OSPF_STUB_MAX_METRIC_SUMMARY_COST; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: use summary max-metric 0x%08x", + __func__, range->cost); + } else if (range->cost_config != OSPF_AREA_RANGE_COST_UNSPEC) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: use configured cost %d", __func__, + range->cost_config); + + range->cost = range->cost_config; + } else { + if (!ospf_area_range_active(range)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: use cost %d", __func__, cost); + + range->cost = cost; /* 1st time get 1st cost */ + } + + if (cost > range->cost) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: update to %d", __func__, cost); + + range->cost = cost; + } + } + + range->specifics++; +} + +static void set_metric(struct ospf_lsa *lsa, uint32_t metric) +{ + struct summary_lsa *header; + uint8_t *mp; + metric = htonl(metric); + mp = (uint8_t *)&metric; + mp++; + header = (struct summary_lsa *)lsa->data; + memcpy(header->metric, mp, 3); +} + +/* ospf_abr_translate_nssa */ +static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_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 ospf_lsa *old = NULL, *new = NULL; + struct as_external_lsa *ext7; + struct prefix_ipv4 p; + struct ospf_area_range *range; + + if (!CHECK_FLAG(lsa->data->options, OSPF_OPTION_NP)) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: LSA Id %pI4, P-bit off, NO Translation", + __func__, &lsa->data->id); + return 1; + } + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: LSA Id %pI4, TRANSLATING 7 to 5", __func__, + &lsa->data->id); + + ext7 = (struct as_external_lsa *)(lsa->data); + p.prefix = lsa->data->id; + p.prefixlen = ip_masklen(ext7->mask); + + if (ext7->e[0].fwd_addr.s_addr == OSPF_DEFAULT_DESTINATION) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "%s: LSA Id %pI4, Forward address is 0, NO Translation", + __func__, &lsa->data->id); + return 1; + } + + /* try find existing AS-External LSA for this prefix */ + old = ospf_external_info_find_lsa(area->ospf, &p); + + if (CHECK_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE)) { + /* if type-7 is removed, remove old translated type-5 lsa */ + if (old) { + UNSET_FLAG(old->flags, OSPF_LSA_APPROVED); + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "%s: remove old translated LSA id %pI4", + __func__, &old->data->id); + } + /* if type-7 is removed and type-5 does not exist, do not + * originate */ + return 1; + } + + range = ospf_area_range_match(area, area->nssa_ranges, &p); + if (range) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("Suppressed by range %pI4/%u of area %pI4", + &range->addr, range->masklen, + &area->area_id); + + ospf_abr_update_aggregate(range, GET_METRIC(ext7->e[0].metric), + area); + return 1; + } + + if (old && CHECK_FLAG(old->flags, OSPF_LSA_APPROVED)) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "%s: found old translated LSA Id %pI4, refreshing", + __func__, &old->data->id); + + /* refresh */ + new = ospf_translated_nssa_refresh(area->ospf, lsa, old); + if (!new) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "%s: could not refresh translated LSA Id %pI4", + __func__, &old->data->id); + } + } else { + /* no existing external route for this LSA Id + * originate translated LSA + */ + + if (ospf_translated_nssa_originate(area->ospf, lsa, old) + == NULL) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "%s: Could not translate Type-7 for %pI4 to Type-5", + __func__, &lsa->data->id); + return 1; + } + } + + return 0; +} + +static void ospf_abr_translate_nssa_range(struct ospf *ospf, + struct prefix_ipv4 *p, uint32_t cost) +{ + struct external_info ei = {}; + struct ospf_lsa *lsa; + + prefix_copy(&ei.p, p); + ei.type = ZEBRA_ROUTE_OSPF; + ei.route_map_set.metric = cost; + ei.route_map_set.metric_type = -1; + + lsa = ospf_external_info_find_lsa(ospf, p); + if (lsa) + lsa = ospf_external_lsa_refresh(ospf, lsa, &ei, + LSA_REFRESH_FORCE, true); + else + lsa = ospf_external_lsa_originate(ospf, &ei); + SET_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT); +} + +void ospf_abr_announce_network_to_area(struct prefix_ipv4 *p, uint32_t cost, + struct ospf_area *area) +{ + struct ospf_lsa *lsa, *old = NULL; + struct summary_lsa *sl = NULL; + uint32_t full_cost; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Start", __func__); + + if (CHECK_FLAG(area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED)) + full_cost = OSPF_STUB_MAX_METRIC_SUMMARY_COST; + else + full_cost = cost; + + old = ospf_lsa_lookup_by_prefix(area->lsdb, OSPF_SUMMARY_LSA, p, + area->ospf->router_id); + if (old) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: old summary found", __func__); + + sl = (struct summary_lsa *)old->data; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: old metric: %d, new metric: %d", + __func__, GET_METRIC(sl->metric), cost); + + if ((GET_METRIC(sl->metric) == full_cost) + && ((old->flags & OSPF_LSA_IN_MAXAGE) == 0)) { + /* unchanged. simply reapprove it */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: old summary approved", + __func__); + SET_FLAG(old->flags, OSPF_LSA_APPROVED); + } else { + /* LSA is changed, refresh it */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: refreshing summary", __func__); + set_metric(old, full_cost); + lsa = ospf_lsa_refresh(area->ospf, old); + + if (!lsa) { + flog_warn(EC_OSPF_LSA_MISSING, + "%s: Could not refresh %pFX to %pI4", + __func__, (struct prefix *)p, + &area->area_id); + return; + } + + SET_FLAG(lsa->flags, OSPF_LSA_APPROVED); + /* This will flood through area. */ + } + } else { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: creating new summary", __func__); + lsa = ospf_summary_lsa_originate(p, full_cost, area); + /* This will flood through area. */ + + if (!lsa) { + flog_warn(EC_OSPF_LSA_MISSING, + "%s: Could not originate %pFX to %pi4", + __func__, (struct prefix *)p, + &area->area_id); + return; + } + + SET_FLAG(lsa->flags, OSPF_LSA_APPROVED); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: flooding new version of summary", + __func__); + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Stop", __func__); +} + +static int ospf_abr_nexthops_belong_to_area(struct ospf_route * or, + struct ospf_area *area) +{ + struct listnode *node, *nnode; + struct ospf_path *path; + struct ospf_interface *oi; + + for (ALL_LIST_ELEMENTS_RO(or->paths, node, path)) + for (ALL_LIST_ELEMENTS_RO(area->oiflist, nnode, oi)) + if (oi->ifp && oi->ifp->ifindex == path->ifindex) + return 1; + + return 0; +} + +static int ospf_abr_should_accept(struct prefix_ipv4 *p, struct ospf_area *area) +{ + if (IMPORT_NAME(area)) { + if (IMPORT_LIST(area) == NULL) + IMPORT_LIST(area) = + access_list_lookup(AFI_IP, IMPORT_NAME(area)); + + if (IMPORT_LIST(area)) + if (access_list_apply(IMPORT_LIST(area), p) + == FILTER_DENY) + return 0; + } + + return 1; +} + +static int ospf_abr_plist_in_check(struct ospf_area *area, + struct ospf_route * or, + struct prefix_ipv4 *p) +{ + if (PREFIX_NAME_IN(area)) { + if (PREFIX_LIST_IN(area) == NULL) + PREFIX_LIST_IN(area) = prefix_list_lookup( + AFI_IP, PREFIX_NAME_IN(area)); + if (PREFIX_LIST_IN(area)) + if (prefix_list_apply(PREFIX_LIST_IN(area), p) + != PREFIX_PERMIT) + return 0; + } + return 1; +} + +static int ospf_abr_plist_out_check(struct ospf_area *area, + struct ospf_route * or, + struct prefix_ipv4 *p) +{ + if (PREFIX_NAME_OUT(area)) { + if (PREFIX_LIST_OUT(area) == NULL) + PREFIX_LIST_OUT(area) = prefix_list_lookup( + AFI_IP, PREFIX_NAME_OUT(area)); + if (PREFIX_LIST_OUT(area)) + if (prefix_list_apply(PREFIX_LIST_OUT(area), p) + != PREFIX_PERMIT) + return 0; + } + return 1; +} + +static void ospf_abr_announce_network(struct ospf *ospf, struct prefix_ipv4 *p, + struct ospf_route * or) +{ + struct ospf_area_range *range; + struct ospf_area *area, *or_area; + struct listnode *node; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Start", __func__); + + or_area = ospf_area_lookup_by_area_id(ospf, or->u.std.area_id); + assert(or_area); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: looking at area %pI4", __func__, + &area->area_id); + + if (IPV4_ADDR_SAME(& or->u.std.area_id, &area->area_id)) + continue; + + if (ospf_abr_nexthops_belong_to_area(or, area)) + continue; + + if (!ospf_abr_should_accept(p, area)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: prefix %pFX was denied by import-list", + __func__, p); + continue; + } + + if (!ospf_abr_plist_in_check(area, or, p)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: prefix %pFX was denied by prefix-list", + __func__, p); + continue; + } + + if (area->external_routing != OSPF_AREA_DEFAULT + && area->no_summary) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: area %pI4 is stub and no_summary", + __func__, &area->area_id); + continue; + } + + if (or->path_type == OSPF_PATH_INTER_AREA) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: this is inter-area route to %pFX", + __func__, p); + + if (!OSPF_IS_AREA_BACKBONE(area)) + ospf_abr_announce_network_to_area(p, or->cost, + area); + } + + if (or->path_type == OSPF_PATH_INTRA_AREA) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: this is intra-area route to %pFX", + __func__, p); + if ((range = ospf_area_range_match( + or_area, or_area->ranges, p)) && + !ospf_area_is_transit(area)) + ospf_abr_update_aggregate(range, or->cost, + area); + else + ospf_abr_announce_network_to_area(p, or->cost, + area); + } + } +} + +static int ospf_abr_should_announce(struct ospf *ospf, struct prefix_ipv4 *p, + struct ospf_route * or) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, or->u.std.area_id); + + assert(area); + + if (EXPORT_NAME(area)) { + if (EXPORT_LIST(area) == NULL) + EXPORT_LIST(area) = + access_list_lookup(AFI_IP, EXPORT_NAME(area)); + + if (EXPORT_LIST(area)) + if (access_list_apply(EXPORT_LIST(area), p) + == FILTER_DENY) + return 0; + } + + return 1; +} + +static void ospf_abr_process_nssa_translates(struct ospf *ospf) +{ + /* 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 ospf_area *area; + struct route_node *rn; + struct ospf_lsa *lsa; + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: Start", __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (!area->NSSATranslatorState) + continue; /* skip if not translator */ + + if (area->external_routing != OSPF_AREA_NSSA) + continue; /* skip if not Nssa Area */ + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s(): looking at area %pI4", __func__, + &area->area_id); + + LSDB_LOOP (NSSA_LSDB(area), rn, lsa) + ospf_abr_translate_nssa(area, lsa); + } + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: Stop", __func__); +} + +static void ospf_abr_process_network_rt(struct ospf *ospf, + struct route_table *rt) +{ + struct ospf_area *area; + struct ospf_route * or ; + struct route_node *rn; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Start", __func__); + + for (rn = route_top(rt); rn; rn = route_next(rn)) { + if ((or = rn->info) == NULL) + continue; + + if (!(area = ospf_area_lookup_by_area_id(ospf, + or->u.std.area_id))) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: area %pI4 no longer exists", __func__, + &or->u.std.area_id); + continue; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: this is a route to %pFX", __func__, + &rn->p); + if (or->path_type >= OSPF_PATH_TYPE1_EXTERNAL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: this is an External router, skipping", + __func__); + continue; + } + + if (or->cost >= OSPF_LS_INFINITY) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: this route's cost is infinity, skipping", + __func__); + continue; + } + + if (or->type == OSPF_DESTINATION_DISCARD) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: this is a discard entry, skipping", + __func__); + continue; + } + + if ( + or->path_type == OSPF_PATH_INTRA_AREA + && !ospf_abr_should_announce( + ospf, (struct prefix_ipv4 *)&rn->p, + or)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: denied by export-list", + __func__); + continue; + } + + if ( + or->path_type == OSPF_PATH_INTRA_AREA + && !ospf_abr_plist_out_check( + area, or, + (struct prefix_ipv4 *)&rn->p)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: denied by prefix-list", + __func__); + continue; + } + + if ((or->path_type == OSPF_PATH_INTER_AREA) + && !OSPF_IS_AREA_ID_BACKBONE(or->u.std.area_id)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: this route is not backbone one, skipping", + __func__); + continue; + } + + + if ((ospf->abr_type == OSPF_ABR_CISCO) + || (ospf->abr_type == OSPF_ABR_IBM)) + + if (!ospf_act_bb_connection(ospf) && + or->path_type != OSPF_PATH_INTRA_AREA) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: ALT ABR: No BB connection, skip not intra-area routes", + __func__); + continue; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: announcing", __func__); + ospf_abr_announce_network(ospf, (struct prefix_ipv4 *)&rn->p, + or); + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Stop", __func__); +} + +static void ospf_abr_announce_rtr_to_area(struct prefix_ipv4 *p, uint32_t cost, + struct ospf_area *area) +{ + struct ospf_lsa *lsa, *old = NULL; + struct summary_lsa *slsa = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Start", __func__); + + old = ospf_lsa_lookup_by_prefix(area->lsdb, OSPF_ASBR_SUMMARY_LSA, p, + area->ospf->router_id); + if (old) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: old summary found", __func__); + slsa = (struct summary_lsa *)old->data; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: old metric: %d, new metric: %d", + __func__, GET_METRIC(slsa->metric), cost); + } + + if (old && (GET_METRIC(slsa->metric) == cost) + && ((old->flags & OSPF_LSA_IN_MAXAGE) == 0)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: old summary approved", __func__); + SET_FLAG(old->flags, OSPF_LSA_APPROVED); + } else { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: 2.2", __func__); + + if (old) { + set_metric(old, cost); + lsa = ospf_lsa_refresh(area->ospf, old); + } else + lsa = ospf_summary_asbr_lsa_originate(p, cost, area); + if (!lsa) { + flog_warn(EC_OSPF_LSA_MISSING, + "%s: Could not refresh/originate %pFX to %pI4", + __func__, (struct prefix *)p, + &area->area_id); + return; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: flooding new version of summary", + __func__); + + /* + zlog_info ("ospf_abr_announce_rtr_to_area(): creating new + summary"); + lsa = ospf_summary_asbr_lsa (p, cost, area, old); */ + + SET_FLAG(lsa->flags, OSPF_LSA_APPROVED); + /* ospf_flood_through_area (area, NULL, lsa);*/ + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Stop", __func__); +} + + +static void ospf_abr_announce_rtr(struct ospf *ospf, struct prefix_ipv4 *p, + struct ospf_route * or) +{ + struct listnode *node; + struct ospf_area *area; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Start", __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: looking at area %pI4", __func__, + &area->area_id); + + if (IPV4_ADDR_SAME(& or->u.std.area_id, &area->area_id)) + continue; + + if (ospf_abr_nexthops_belong_to_area(or, area)) + continue; + + /* RFC3101: Do not generate ASBR type 4 LSA if NSSA ABR */ + if (or->u.std.external_routing == OSPF_AREA_NSSA) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: do not generate LSA Type-4 %pI4 from NSSA", + __func__, &p->prefix); + continue; + } + + if (area->external_routing != OSPF_AREA_DEFAULT) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: area %pI4 doesn't support external routing", + __func__, &area->area_id); + continue; + } + + if (or->path_type == OSPF_PATH_INTER_AREA) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: this is inter-area route to %pI4", + __func__, &p->prefix); + if (!OSPF_IS_AREA_BACKBONE(area)) + ospf_abr_announce_rtr_to_area(p, or->cost, + area); + } + + if (or->path_type == OSPF_PATH_INTRA_AREA) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: this is intra-area route to %pI4", + __func__, &p->prefix); + ospf_abr_announce_rtr_to_area(p, or->cost, area); + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Stop", __func__); +} + +static void ospf_abr_process_router_rt(struct ospf *ospf, + struct route_table *rt) +{ + struct ospf_route * or ; + struct route_node *rn; + struct list *l; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Start", __func__); + + for (rn = route_top(rt); rn; rn = route_next(rn)) { + struct listnode *node, *nnode; + char flag = 0; + struct ospf_route *best = NULL; + + if (rn->info == NULL) + continue; + + l = rn->info; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: this is a route to %pI4", __func__, + &rn->p.u.prefix4); + + for (ALL_LIST_ELEMENTS(l, node, nnode, or)) { + if (!ospf_area_lookup_by_area_id(ospf, + or->u.std.area_id)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: area %pI4 no longer exists", __func__, + &or->u.std.area_id); + continue; + } + + + if (!CHECK_FLAG(or->u.std.flags, ROUTER_LSA_EXTERNAL)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: This is not an ASBR, skipping", + __func__); + continue; + } + + if (!flag) { + best = ospf_find_asbr_route( + ospf, rt, (struct prefix_ipv4 *)&rn->p); + flag = 1; + } + + if (best == NULL) + continue; + + if (or != best) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: This route is not the best among possible, skipping", + __func__); + continue; + } + + if ( + or->path_type == OSPF_PATH_INTER_AREA + && !OSPF_IS_AREA_ID_BACKBONE( + or->u.std.area_id)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: This route is not a backbone one, skipping", + __func__); + continue; + } + + if (or->cost >= OSPF_LS_INFINITY) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: This route has LS_INFINITY metric, skipping", + __func__); + continue; + } + + if (ospf->abr_type == OSPF_ABR_CISCO + || ospf->abr_type == OSPF_ABR_IBM) + if (!ospf_act_bb_connection(ospf) && + or->path_type != OSPF_PATH_INTRA_AREA) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: ALT ABR: No BB connection, skip not intra-area routes", + __func__); + continue; + } + + ospf_abr_announce_rtr(ospf, + (struct prefix_ipv4 *)&rn->p, or); + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Stop", __func__); +} + +static void +ospf_abr_unapprove_translates(struct ospf *ospf) /* For NSSA Translations */ +{ + struct ospf_lsa *lsa; + struct route_node *rn; + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: Start", __func__); + + /* NSSA Translator is not checked, because it may have gone away, + and we would want to flush any residuals anyway */ + + LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) + if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT)) { + UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED); + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: approved unset on link id %pI4", + __func__, &lsa->data->id); + } + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: Stop", __func__); +} + +static void ospf_abr_unapprove_summaries(struct ospf *ospf) +{ + struct listnode *node; + struct ospf_area *area; + struct route_node *rn; + struct ospf_lsa *lsa; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Start", __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: considering area %pI4", __func__, + &area->area_id); + LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa) + if (ospf_lsa_is_self_originated(ospf, lsa)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: approved unset on summary link id %pI4", + __func__, &lsa->data->id); + UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED); + } + + LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa) + if (ospf_lsa_is_self_originated(ospf, lsa)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: approved unset on asbr-summary link id %pI4", + __func__, &lsa->data->id); + UNSET_FLAG(lsa->flags, OSPF_LSA_APPROVED); + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Stop", __func__); +} + +static void ospf_abr_prepare_aggregates(struct ospf *ospf, bool nssa) +{ + struct listnode *node; + struct route_node *rn; + struct ospf_area_range *range; + struct ospf_area *area; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Start", __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + struct route_table *ranges; + + if (nssa) + ranges = area->nssa_ranges; + else + ranges = area->ranges; + + for (rn = route_top(ranges); rn; rn = route_next(rn)) + if ((range = rn->info) != NULL) { + range->cost = 0; + range->specifics = 0; + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Stop", __func__); +} + +static void ospf_abr_announce_aggregates(struct ospf *ospf) +{ + struct ospf_area *area, *ar; + struct ospf_area_range *range; + struct route_node *rn; + struct prefix p; + struct listnode *node, *n; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Start", __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: looking at area %pI4", __func__, + &area->area_id); + + for (rn = route_top(area->ranges); rn; rn = route_next(rn)) + if ((range = rn->info)) { + if (!CHECK_FLAG(range->flags, + OSPF_AREA_RANGE_ADVERTISE)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: discarding suppress-ranges", + __func__); + continue; + } + + p.family = AF_INET; + p.u.prefix4 = range->addr; + p.prefixlen = range->masklen; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: this is range: %pFX", + __func__, &p); + + if (CHECK_FLAG(range->flags, + OSPF_AREA_RANGE_SUBSTITUTE)) { + p.family = AF_INET; + p.u.prefix4 = range->subst_addr; + p.prefixlen = range->subst_masklen; + } + + if (ospf_area_range_active(range)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: active range", + __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, + n, ar)) { + if (ar == area) + continue; + + /* We do not check nexthops + here, because + intra-area routes can be + associated with + one area only */ + + /* backbone routes are not + summarized + when announced into transit + areas */ + + if (ospf_area_is_transit(ar) + && OSPF_IS_AREA_BACKBONE( + area)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: Skipping announcement of BB aggregate into a transit area", + __func__); + continue; + } + ospf_abr_announce_network_to_area( + (struct prefix_ipv4 + *)&p, + range->cost, ar); + } + } + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Stop", __func__); +} + +static void ospf_abr_send_nssa_aggregates(struct ospf *ospf) +{ + struct listnode *node; + struct ospf_area *area; + struct route_node *rn; + struct prefix_ipv4 p; + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: Start", __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (!area->NSSATranslatorState) + continue; + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: looking at area %pI4", __func__, + &area->area_id); + + for (rn = route_top(area->nssa_ranges); rn; + rn = route_next(rn)) { + struct ospf_area_range *range; + + range = rn->info; + if (!range) + continue; + + p.family = AF_INET; + p.prefix = range->addr; + p.prefixlen = range->masklen; + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: this is range: %pFX", __func__, + &p); + + if (ospf_area_range_active(range) + && CHECK_FLAG(range->flags, + OSPF_AREA_RANGE_ADVERTISE)) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: active range", + __func__); + + /* Fetch LSA-Type-7 from aggregate prefix, and + * then + * translate, Install (as Type-5), Approve, and + * Flood + */ + ospf_abr_translate_nssa_range(ospf, &p, + range->cost); + } + } /* all area ranges*/ + } /* all areas */ + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: Stop", __func__); +} + +static void ospf_abr_announce_stub_defaults(struct ospf *ospf) +{ + struct listnode *node; + struct ospf_area *area; + struct prefix_ipv4 p; + + if (!IS_OSPF_ABR(ospf)) + return; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Start", __func__); + + p.family = AF_INET; + p.prefix.s_addr = OSPF_DEFAULT_DESTINATION; + p.prefixlen = 0; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: looking at area %pI4", __func__, + &area->area_id); + + if ((area->external_routing != OSPF_AREA_STUB) + && (area->external_routing != OSPF_AREA_NSSA)) + continue; + + if (OSPF_IS_AREA_BACKBONE(area)) + continue; /* Sanity Check */ + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: announcing 0.0.0.0/0 to area %pI4", + __func__, &area->area_id); + ospf_abr_announce_network_to_area(&p, area->default_cost, area); + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Stop", __func__); +} + +/** @brief Function to check and generate indication + * LSA for area on which we received + * indication LSA flush. + * @param Ospf instance. + * @param Area on which indication lsa flush is to be generated. + * @return Void. + */ +void ospf_generate_indication_lsa(struct ospf *ospf, struct ospf_area *area) +{ + bool area_fr_not_supp = false; + + /* Check if you have any area which doesn't support + * flood reduction. + */ + + area_fr_not_supp = ospf_check_fr_enabled_all(ospf) ? false : true; + + /* If any one of the area doestn't support FR, generate + * indication LSA on behalf of that area. + */ + + if (area_fr_not_supp && !area->fr_info.area_ind_lsa_recvd && + !area->fr_info.indication_lsa_self && + !area->fr_info.area_dc_clear) { + + struct prefix_ipv4 p; + struct ospf_lsa *new; + + p.family = AF_INET; + p.prefix = ospf->router_id; + p.prefixlen = IPV4_MAX_BITLEN; + + new = ospf_summary_asbr_lsa_originate(&p, OSPF_LS_INFINITY, + area); + if (!new) { + zlog_debug("%s: Indication lsa originate failed", + __func__); + return; + } + /* save the indication lsa for that area */ + area->fr_info.indication_lsa_self = new; + } +} + +/** @brief Function to receive and process indication LSA + * flush from area. + * @param lsa being flushed. + * @return Void. + */ +void ospf_recv_indication_lsa_flush(struct ospf_lsa *lsa) +{ + if (!IS_LSA_SELF(lsa) && IS_LSA_MAXAGE(lsa) && + ospf_check_indication_lsa(lsa)) { + lsa->area->fr_info.area_ind_lsa_recvd = false; + + OSPF_LOG_INFO("%s: Received an ind lsa: %pI4 area %pI4", + __func__, &lsa->data->id, &lsa->area->area_id); + + if (!IS_OSPF_ABR(lsa->area->ospf)) + return; + + /* If the LSA received is a indication LSA with maxage on + * the network, then check and regenerate indication + * LSA if any of our areas don't support flood reduction. + */ + ospf_generate_indication_lsa(lsa->area->ospf, lsa->area); + } +} + +/** @brief Function to generate indication LSAs. + * @param Ospf instance. + * @param Area on behalf of which indication + * LSA is generated LSA. + * @return Void. + */ +void ospf_abr_generate_indication_lsa(struct ospf *ospf, + const struct ospf_area *area) +{ + struct ospf_lsa *new; + struct listnode *node; + struct ospf_area *o_area; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, o_area)) { + if (o_area == area) + continue; + + if (o_area->fr_info.indication_lsa_self || + o_area->fr_info.area_ind_lsa_recvd || + o_area->fr_info.area_dc_clear) { + /* if the area has already received an + * indication LSA or if area already has + * LSAs with DC bit 0 other than + * indication LSA then don't generate + * indication LSA in those areas. + */ + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "Area %pI4 has LSAs with dc bit clear", + &o_area->area_id); + continue; + + } else { + + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefix = ospf->router_id; + p.prefixlen = IPV4_MAX_BITLEN; + + new = ospf_summary_asbr_lsa_originate( + &p, OSPF_LS_INFINITY, o_area); + if (!new) { + zlog_debug( + "%s: Indication lsa originate Failed", + __func__); + return; + } + /* save the indication lsa for that area */ + o_area->fr_info.indication_lsa_self = new; + } + } +} + +/** @brief Flush the indication LSA from all the areas + * of ospf instance. + * @param Ospf instance. + * @return Void. + */ +void ospf_flush_indication_lsas(struct ospf *ospf) +{ + struct ospf_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (area->fr_info.indication_lsa_self) { + OSPF_LOG_INFO( + "Flushing ind lsa: %pI4 area %pI4", + &area->fr_info.indication_lsa_self->data->id, + &area->area_id); + ospf_schedule_lsa_flush_area( + area, area->fr_info.indication_lsa_self); + area->fr_info.indication_lsa_self = NULL; + } + } +} + +/** @brief Check if flood reduction is enabled on + * all the areas. + * @param Ospf instance. + * @return Void. + */ +bool ospf_check_fr_enabled_all(struct ospf *ospf) +{ + const struct ospf_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) + if (!ospf_check_area_fr_enabled(area)) + return false; + + return true; +} + +/** @brief Abr function to check conditions for generation + * of indication. LSAs/announcing non-DNA routers + * in the area. + * @param thread + * @return 0. + */ +static void ospf_abr_announce_non_dna_routers(struct event *thread) +{ + struct ospf_area *area; + struct listnode *node; + struct ospf *ospf = EVENT_ARG(thread); + + EVENT_OFF(ospf->t_abr_fr); + + if (!IS_OSPF_ABR(ospf)) + return; + + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, "%s(): Start", __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "%s: Area %pI4 FR enabled: %d", __func__, + &area->area_id, area->fr_info.enabled); + OSPF_LOG_DEBUG( + IS_DEBUG_OSPF_EVENT, + "LSA with DC bit clear: %d Recived indication LSA: %d", + area->fr_info.area_dc_clear, + area->fr_info.area_ind_lsa_recvd); + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, "FR state change: %d", + area->fr_info.state_changed); + if (!OSPF_IS_AREA_BACKBONE(area) && + area->fr_info.area_dc_clear) { + /* rfc4136 rfc1793: Suppose if the abr is connected to + * a regular non-backbone OSPF area, Furthermore if + * the area has LSAs with the DC-bit clear, other + * than indication-LSAs. Then originate indication-LSAs + * into all other directly-connected "regular" areas, + * including the backbone area. + */ + ospf_abr_generate_indication_lsa(ospf, area); + } + + if (OSPF_IS_AREA_BACKBONE(area) && + (area->fr_info.area_dc_clear || + area->fr_info.area_ind_lsa_recvd)) { + /* rfc4136 rfc1793: Suppose if the abr is connected to + * backbone OSPF area. Furthermore, if backbone has + * LSAs with the DC-bit clear that are either + * a) not indication-LSAs or indication-LSAs or + * b) indication-LSAs that have been originated by + * other routers, + * then originate indication-LSAs into all other + * directly-connected "regular" non-backbone areas. + */ + ospf_abr_generate_indication_lsa(ospf, area); + } + + if (area->fr_info.enabled && area->fr_info.state_changed && + area->fr_info.indication_lsa_self) { + /* Ospf area flood reduction state changed + * area now supports flood reduction. + * check if all other areas support flood reduction + * if yes then flush indication LSAs generated in + * all the areas. + */ + if (ospf_check_fr_enabled_all(ospf)) + ospf_flush_indication_lsas(ospf); + + area->fr_info.state_changed = false; + } + + /* If previously we had generated indication lsa + * but now area has lsas with dc bit set to 0 + * apart from indication lsa, we'll clear indication lsa + */ + if (area->fr_info.area_dc_clear && + area->fr_info.indication_lsa_self) { + ospf_schedule_lsa_flush_area( + area, area->fr_info.indication_lsa_self); + area->fr_info.indication_lsa_self = NULL; + } + } + + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, "%s(): Stop", __func__); +} + +static void ospf_abr_nssa_type7_default_create(struct ospf *ospf, + struct ospf_area *area, + struct ospf_lsa *lsa) +{ + struct external_info ei; + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "Announcing Type-7 default route into NSSA area %pI4", + &area->area_id); + + /* Prepare the extrenal_info for aggregator */ + memset(&ei, 0, sizeof(struct external_info)); + ei.p.family = AF_INET; + ei.p.prefixlen = 0; + ei.tag = 0; + ei.type = 0; + ei.instance = ospf->instance; + + /* Compute default route type and metric. */ + if (area->nssa_default_originate.metric_value != -1) + ei.route_map_set.metric = + area->nssa_default_originate.metric_value; + else + ei.route_map_set.metric = DEFAULT_DEFAULT_ALWAYS_METRIC; + if (area->nssa_default_originate.metric_type != -1) + ei.route_map_set.metric_type = + area->nssa_default_originate.metric_type; + else + ei.route_map_set.metric_type = DEFAULT_METRIC_TYPE; + + if (!lsa) + ospf_nssa_lsa_originate(area, &ei); + else + ospf_nssa_lsa_refresh(area, lsa, &ei); +} + +static void ospf_abr_nssa_type7_default_delete(struct ospf *ospf, + struct ospf_area *area, + struct ospf_lsa *lsa) +{ + if (lsa && !CHECK_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE)) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "Withdrawing Type-7 default route from area %pI4", + &area->area_id); + + ospf_ls_retransmit_delete_nbr_area(area, lsa); + ospf_refresher_unregister_lsa(ospf, lsa); + ospf_lsa_flush_area(lsa, area); + } +} + +/* NSSA Type-7 default route. */ +void ospf_abr_nssa_type7_defaults(struct ospf *ospf) +{ + struct ospf_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + struct in_addr id = {}; + struct ospf_lsa *lsa; + + lsa = ospf_lsdb_lookup_by_id(area->lsdb, OSPF_AS_NSSA_LSA, id, + area->ospf->router_id); + if (area->external_routing == OSPF_AREA_NSSA + && area->nssa_default_originate.enabled + && (IS_OSPF_ABR(ospf) + || (IS_OSPF_ASBR(ospf) + && ospf->nssa_default_import_check.status))) + ospf_abr_nssa_type7_default_create(ospf, area, lsa); + else + ospf_abr_nssa_type7_default_delete(ospf, area, lsa); + } +} + +static int ospf_abr_remove_unapproved_translates_apply(struct ospf *ospf, + struct ospf_lsa *lsa) +{ + if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT) + && !CHECK_FLAG(lsa->flags, OSPF_LSA_APPROVED)) { + zlog_info("%s: removing unapproved translates, ID: %pI4", + __func__, &lsa->data->id); + + /* FLUSH THROUGHOUT AS */ + ospf_lsa_flush_as(ospf, lsa); + + /* DISCARD from LSDB */ + } + return 0; +} + +static void ospf_abr_remove_unapproved_translates(struct ospf *ospf) +{ + struct route_node *rn; + struct ospf_lsa *lsa; + + /* All AREA PROCESS should have APPROVED necessary LSAs */ + /* Remove any left over and not APPROVED */ + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: Start", __func__); + + LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) + ospf_abr_remove_unapproved_translates_apply(ospf, lsa); + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: Stop", __func__); +} + +static void ospf_abr_remove_unapproved_summaries(struct ospf *ospf) +{ + struct listnode *node; + struct ospf_area *area; + struct route_node *rn; + struct ospf_lsa *lsa; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Start", __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: looking at area %pI4", __func__, + &area->area_id); + + LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa) + if (ospf_lsa_is_self_originated(ospf, lsa)) + if (!CHECK_FLAG(lsa->flags, OSPF_LSA_APPROVED)) + ospf_lsa_flush_area(lsa, area); + + LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa) + if (ospf_lsa_is_self_originated(ospf, lsa) && + !CHECK_FLAG(lsa->flags, OSPF_LSA_APPROVED) && + /* Do not remove indication LSAs while + * flushing unapproved summaries. + */ + !ospf_check_indication_lsa(lsa)) + ospf_lsa_flush_area(lsa, area); + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Stop", __func__); +} + +static void ospf_abr_manage_discard_routes(struct ospf *ospf, bool nssa) +{ + struct listnode *node, *nnode; + struct route_node *rn; + struct ospf_area *area; + + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + struct route_table *ranges; + + if (nssa) + ranges = area->nssa_ranges; + else + ranges = area->ranges; + + for (rn = route_top(ranges); rn; rn = route_next(rn)) { + struct ospf_area_range *range; + + range = rn->info; + if (!range) + continue; + + if (ospf_area_range_active(range) + && CHECK_FLAG(range->flags, + OSPF_AREA_RANGE_ADVERTISE)) + ospf_add_discard_route( + ospf, ospf->new_table, area, + (struct prefix_ipv4 *)&rn->p, nssa); + else + ospf_delete_discard_route( + ospf, ospf->new_table, + (struct prefix_ipv4 *)&rn->p, nssa); + } + } +} + +/* This is the function taking care about ABR NSSA, i.e. NSSA + Translator, -LSA aggregation and flooding. For all NSSAs + + Any SELF-AS-LSA is in the Type-5 LSDB and Type-7 LSDB. These LSA's + are refreshed from the Type-5 LSDB, installed into the Type-7 LSDB + with the P-bit set. + + Any received Type-5s are legal for an ABR, else illegal for IR. + Received Type-7s are installed, by area, with incoming P-bit. They + are flooded; if the Elected NSSA Translator, then P-bit off. + + Additionally, this ABR will place "translated type-7's" into the + Type-5 LSDB in order to keep track of APPROVAL or not. + + It will scan through every area, looking for Type-7 LSAs with P-Bit + SET. The Type-7's are either AS-FLOODED & 5-INSTALLED or + AGGREGATED. Later, the AGGREGATED LSAs are AS-FLOODED & + 5-INSTALLED. + + 5-INSTALLED is into the Type-5 LSDB; Any UNAPPROVED Type-5 LSAs + left over are FLUSHED and DISCARDED. + + For External Calculations, any NSSA areas use the Type-7 AREA-LSDB, + any ABR-non-NSSA areas use the Type-5 GLOBAL-LSDB. */ + +void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */ +{ + if (ospf->gr_info.restart_in_progress) + return; + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("Check for NSSA-ABR Tasks():"); + + if (!IS_OSPF_ABR(ospf)) + return; + + if (!ospf->anyNSSA) + return; + + /* Each area must confirm TranslatorRole */ + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: Start", __func__); + + /* For all Global Entries flagged "local-translate", unset APPROVED */ + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: unapprove translates", __func__); + + ospf_abr_unapprove_translates(ospf); + + /* RESET all Ranges in every Area, same as summaries */ + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: NSSA initialize aggregates", __func__); + ospf_abr_prepare_aggregates(ospf, true); + + /* 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_DEBUG_OSPF_NSSA) + zlog_debug("%s: process translates", __func__); + ospf_abr_process_nssa_translates(ospf); + + /* 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_DEBUG_OSPF_NSSA) + zlog_debug("%s: send NSSA aggregates", __func__); + ospf_abr_send_nssa_aggregates(ospf); /*TURNED OFF FOR NOW */ + + /* Send any NSSA defaults as Type-5 + *if (IS_DEBUG_OSPF_NSSA) + * zlog_debug ("ospf_abr_nssa_task(): announce nssa defaults"); + *ospf_abr_announce_nssa_defaults (ospf); + * havnt a clue what above is supposed to do. + */ + + /* Flush any unapproved previous translates from Global Data Base */ + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: remove unapproved translates", __func__); + ospf_abr_remove_unapproved_translates(ospf); + + ospf_abr_manage_discard_routes(ospf, true); + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: Stop", __func__); +} + +/* This is the function taking care about ABR stuff, i.e. + summary-LSA origination and flooding. */ +void ospf_abr_task(struct ospf *ospf) +{ + if (ospf->gr_info.restart_in_progress) + return; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Start", __func__); + + if (ospf->new_table == NULL || ospf->new_rtrs == NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Routing tables are not yet ready", + __func__); + return; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: unapprove summaries", __func__); + ospf_abr_unapprove_summaries(ospf); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: prepare aggregates", __func__); + ospf_abr_prepare_aggregates(ospf, false); + + if (IS_OSPF_ABR(ospf)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: process network RT", __func__); + ospf_abr_process_network_rt(ospf, ospf->new_table); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: process router RT", __func__); + ospf_abr_process_router_rt(ospf, ospf->new_rtrs); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: announce aggregates", __func__); + ospf_abr_announce_aggregates(ospf); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: announce stub defaults", __func__); + ospf_abr_announce_stub_defaults(ospf); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: announce NSSA Type-7 defaults", + __func__); + ospf_abr_nssa_type7_defaults(ospf); + + if (ospf->fr_configured) { + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "%s(): announce non-DNArouters", + __func__); + /* + * Schedule indication lsa generation timer, + * giving time for route synchronization in + * all the routers. + */ + event_add_timer(master, + ospf_abr_announce_non_dna_routers, ospf, + OSPF_ABR_DNA_TIMER, &ospf->t_abr_fr); + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: remove unapproved summaries", __func__); + ospf_abr_remove_unapproved_summaries(ospf); + + ospf_abr_manage_discard_routes(ospf, false); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Stop", __func__); +} + +static void ospf_abr_task_timer(struct event *thread) +{ + struct ospf *ospf = EVENT_ARG(thread); + + ospf->t_abr_task = 0; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Running ABR task on timer"); + + ospf_check_abr_status(ospf); + ospf_abr_nssa_check_status(ospf); + + ospf_abr_task(ospf); + ospf_abr_nssa_task(ospf); /* if nssa-abr, then scan Type-7 LSDB */ +} + +void ospf_schedule_abr_task(struct ospf *ospf) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Scheduling ABR task"); + + event_add_timer(master, ospf_abr_task_timer, ospf, OSPF_ABR_TASK_DELAY, + &ospf->t_abr_task); +} diff --git a/ospfd/ospf_abr.h b/ospfd/ospf_abr.h new file mode 100644 index 0000000..cc2b2b0 --- /dev/null +++ b/ospfd/ospf_abr.h @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF ABR functions. + * Copyright (C) 1999 Alex Zinin + */ + +#ifndef _ZEBRA_OSPF_ABR_H +#define _ZEBRA_OSPF_ABR_H + +#define OSPF_ABR_TASK_DELAY 5 +#define OSPF_ABR_DNA_TIMER 10 +/* Delay in announceing Non-DNA routers + * so that LSAs are completely synced + * before generating indication LSAs. + */ + +#define OSPF_AREA_RANGE_ADVERTISE (1 << 0) +#define OSPF_AREA_RANGE_SUBSTITUTE (1 << 1) +#define OSPF_AREA_RANGE_NSSA (1 << 2) + +/* Area range. */ +struct ospf_area_range { + /* Area range address. */ + struct in_addr addr; + + /* Area range masklen. */ + uint8_t masklen; + + /* Flags. */ + uint8_t flags; + + /* Number of more specific prefixes. */ + int specifics; + + /* Addr and masklen to substitute. */ + struct in_addr subst_addr; + uint8_t subst_masklen; + + /* Range cost. */ + uint32_t cost; + + /* Configured range cost. */ + uint32_t cost_config; +}; + +/* Prototypes. */ +extern struct ospf_area_range *ospf_area_range_lookup(struct ospf_area *, + struct route_table *, + struct prefix_ipv4 *); +extern struct ospf_area_range * +ospf_area_range_lookup_next(struct ospf_area *, struct in_addr *, int); + +extern int ospf_area_range_set(struct ospf *, struct ospf_area *, + struct route_table *, struct prefix_ipv4 *, int, + bool); +extern int ospf_area_range_cost_set(struct ospf *, struct ospf_area *, + struct route_table *, struct prefix_ipv4 *, + uint32_t); +extern int ospf_area_range_unset(struct ospf *, struct ospf_area *, + struct route_table *, struct prefix_ipv4 *); +extern int ospf_area_range_substitute_set(struct ospf *, struct ospf_area *, + struct prefix_ipv4 *, + struct prefix_ipv4 *); +extern int ospf_area_range_substitute_unset(struct ospf *, struct ospf_area *, + struct prefix_ipv4 *); +extern struct ospf_area_range *ospf_area_range_match_any(struct ospf *, + struct prefix_ipv4 *); +extern int ospf_area_range_active(struct ospf_area_range *); +extern int ospf_act_bb_connection(struct ospf *); + +extern void ospf_check_abr_status(struct ospf *); +extern void ospf_abr_task(struct ospf *); +extern void ospf_abr_nssa_task(struct ospf *ospf); +extern void ospf_schedule_abr_task(struct ospf *); + +extern void ospf_abr_announce_network_to_area(struct prefix_ipv4 *, uint32_t, + struct ospf_area *); +extern void ospf_abr_nssa_type7_defaults(struct ospf *ospf); +extern void ospf_abr_nssa_check_status(struct ospf *ospf); +extern void ospf_abr_generate_indication_lsa(struct ospf *ospf, + const struct ospf_area *area); +extern void ospf_flush_indication_lsas(struct ospf *ospf); +extern void ospf_generate_indication_lsa(struct ospf *ospf, + struct ospf_area *area); +extern bool ospf_check_fr_enabled_all(struct ospf *ospf); +extern void ospf_recv_indication_lsa_flush(struct ospf_lsa *lsa); + +/** @brief Static inline functions. + * @param Area pointer. + * @return area Flood Reduction status. + */ +static inline bool ospf_check_area_fr_enabled(const struct ospf_area *area) +{ + return area->fr_info.enabled ? true : false; +} +#endif /* _ZEBRA_OSPF_ABR_H */ diff --git a/ospfd/ospf_api.c b/ospfd/ospf_api.c new file mode 100644 index 0000000..213ee8c --- /dev/null +++ b/ospfd/ospf_api.c @@ -0,0 +1,680 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * API message handling module for OSPF daemon and client. + * Copyright (C) 2001, 2002 Ralph Keller + * Copyright (c) 2022, LabN Consulting, L.L.C. + */ + +#include + +#ifdef SUPPORT_OSPF_API + +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "memory.h" +#include "command.h" +#include "vty.h" +#include "stream.h" +#include "log.h" +#include "frrevent.h" +#include "hash.h" +#include "sockunion.h" /* for inet_aton() */ +#include "buffer.h" +#include "network.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_zebra.h" + +#include "ospfd/ospf_api.h" + + +/* For debugging only, will be removed */ +void api_opaque_lsa_print(struct ospf_lsa *lsa) +{ + struct opaque_lsa { + struct lsa_header header; + uint8_t mydata[]; + }; + + struct opaque_lsa *olsa; + int opaquelen; + int i; + + ospf_lsa_header_dump(lsa->data); + + olsa = (struct opaque_lsa *)lsa->data; + + opaquelen = lsa->size - OSPF_LSA_HEADER_SIZE; + zlog_debug("apiserver_lsa_print: opaquelen=%d", opaquelen); + + for (i = 0; i < opaquelen; i++) { + zlog_debug("0x%x ", olsa->mydata[i]); + } + zlog_debug(" "); +} + +/* ----------------------------------------------------------- + * Generic messages + * ----------------------------------------------------------- + */ + +struct msg *msg_new(uint8_t msgtype, void *msgbody, uint32_t seqnum, + uint16_t msglen) +{ + struct msg *new; + + new = XCALLOC(MTYPE_OSPF_API_MSG, sizeof(struct msg)); + + new->hdr.version = OSPF_API_VERSION; + new->hdr.msgtype = msgtype; + new->hdr.msglen = htons(msglen); + new->hdr.msgseq = htonl(seqnum); + + new->s = stream_new(msglen); + assert(new->s); + stream_put(new->s, msgbody, msglen); + + return new; +} + + +/* Duplicate a message by copying content. */ +struct msg *msg_dup(struct msg *msg) +{ + struct msg *new; + size_t size; + + assert(msg); + + size = ntohs(msg->hdr.msglen); + if (size > OSPF_MAX_LSA_SIZE) + return NULL; + + new = msg_new(msg->hdr.msgtype, STREAM_DATA(msg->s), + ntohl(msg->hdr.msgseq), size); + return new; +} + + +/* XXX only for testing, will be removed */ + +struct nametab { + int value; + const char *name; +}; + +const char *ospf_api_typename(int msgtype) +{ + struct nametab NameTab[] = { + { + MSG_REGISTER_OPAQUETYPE, "Register opaque-type", + }, + { + MSG_UNREGISTER_OPAQUETYPE, "Unregister opaque-type", + }, + { + MSG_REGISTER_EVENT, "Register event", + }, + { + MSG_SYNC_LSDB, "Sync LSDB", + }, + { + MSG_ORIGINATE_REQUEST, "Originate request", + }, + { + MSG_DELETE_REQUEST, "Delete request", + }, + { + MSG_REPLY, "Reply", + }, + { + MSG_READY_NOTIFY, "Ready notify", + }, + { + MSG_LSA_UPDATE_NOTIFY, "LSA update notify", + }, + { + MSG_LSA_DELETE_NOTIFY, "LSA delete notify", + }, + { + MSG_NEW_IF, "New interface", + }, + { + MSG_DEL_IF, "Del interface", + }, + { + MSG_ISM_CHANGE, "ISM change", + }, + { + MSG_NSM_CHANGE, "NSM change", + }, + { + MSG_REACHABLE_CHANGE, + "Reachable change", + }, + }; + + int i, n = array_size(NameTab); + const char *name = NULL; + + for (i = 0; i < n; i++) { + if (NameTab[i].value == msgtype) { + name = NameTab[i].name; + break; + } + } + + return name ? name : "?"; +} + +const char *ospf_api_errname(int errcode) +{ + struct nametab NameTab[] = { + { + OSPF_API_OK, "OK", + }, + { + OSPF_API_NOSUCHINTERFACE, "No such interface", + }, + { + OSPF_API_NOSUCHAREA, "No such area", + }, + { + OSPF_API_NOSUCHLSA, "No such LSA", + }, + { + OSPF_API_ILLEGALLSATYPE, "Illegal LSA type", + }, + { + OSPF_API_OPAQUETYPEINUSE, "Opaque type in use", + }, + { + OSPF_API_OPAQUETYPENOTREGISTERED, + "Opaque type not registered", + }, + { + OSPF_API_NOTREADY, "Not ready", + }, + { + OSPF_API_NOMEMORY, "No memory", + }, + { + OSPF_API_ERROR, "Other error", + }, + { + OSPF_API_UNDEF, "Undefined", + }, + }; + + int i, n = array_size(NameTab); + const char *name = NULL; + + for (i = 0; i < n; i++) { + if (NameTab[i].value == errcode) { + name = NameTab[i].name; + break; + } + } + + return name ? name : "?"; +} + +void msg_print(struct msg *msg) +{ + if (!msg) { + zlog_debug("msg_print msg=NULL!"); + return; + } + + /* API message common header part. */ + zlog_debug("API-msg [%s]: type(%d),len(%d),seq(%lu),data(%p),size(%zd)", + ospf_api_typename(msg->hdr.msgtype), msg->hdr.msgtype, + ntohs(msg->hdr.msglen), + (unsigned long)ntohl(msg->hdr.msgseq), STREAM_DATA(msg->s), + STREAM_SIZE(msg->s)); + + return; +} + +void msg_free(struct msg *msg) +{ + if (msg->s) + stream_free(msg->s); + + XFREE(MTYPE_OSPF_API_MSG, msg); +} + + +/* Set sequence number of message */ +void msg_set_seq(struct msg *msg, uint32_t seqnr) +{ + assert(msg); + msg->hdr.msgseq = htonl(seqnr); +} + +/* Get sequence number of message */ +uint32_t msg_get_seq(struct msg *msg) +{ + assert(msg); + return ntohl(msg->hdr.msgseq); +} + +/* ----------------------------------------------------------- + * Message fifo queues + * ----------------------------------------------------------- + */ + +struct msg_fifo *msg_fifo_new(void) +{ + return XCALLOC(MTYPE_OSPF_API_FIFO, sizeof(struct msg_fifo)); +} + +/* Add new message to fifo. */ +void msg_fifo_push(struct msg_fifo *fifo, struct msg *msg) +{ + if (fifo->tail) + fifo->tail->next = msg; + else + fifo->head = msg; + + fifo->tail = msg; + fifo->count++; +} + + +/* Remove first message from fifo. */ +struct msg *msg_fifo_pop(struct msg_fifo *fifo) +{ + struct msg *msg; + + msg = fifo->head; + if (msg) { + fifo->head = msg->next; + + if (fifo->head == NULL) + fifo->tail = NULL; + + fifo->count--; + } + return msg; +} + +/* Return first fifo entry but do not remove it. */ +struct msg *msg_fifo_head(struct msg_fifo *fifo) +{ + return fifo->head; +} + +/* Flush message fifo. */ +void msg_fifo_flush(struct msg_fifo *fifo) +{ + struct msg *op; + struct msg *next; + + for (op = fifo->head; op; op = next) { + next = op->next; + msg_free(op); + } + + fifo->head = fifo->tail = NULL; + fifo->count = 0; +} + +/* Free API message fifo. */ +void msg_fifo_free(struct msg_fifo *fifo) +{ + msg_fifo_flush(fifo); + + XFREE(MTYPE_OSPF_API_FIFO, fifo); +} + +struct msg *msg_read(int fd) +{ + struct msg *msg; + struct apimsghdr hdr; + uint8_t buf[OSPF_API_MAX_MSG_SIZE]; + ssize_t bodylen; + ssize_t rlen; + + /* Read message header */ + rlen = readn(fd, (uint8_t *)&hdr, sizeof(struct apimsghdr)); + + if (rlen < 0) { + zlog_warn("msg_read: readn %s", safe_strerror(errno)); + return NULL; + } else if (rlen == 0) { + zlog_warn("msg_read: Connection closed by peer"); + return NULL; + } else if (rlen != sizeof(struct apimsghdr)) { + zlog_warn("msg_read: Cannot read message header!"); + return NULL; + } + + /* Check version of API protocol */ + if (hdr.version != OSPF_API_VERSION) { + zlog_warn("msg_read: OSPF API protocol version mismatch"); + return NULL; + } + + /* Determine body length. */ + bodylen = ntohs(hdr.msglen); + if (bodylen > (ssize_t)sizeof(buf)) { + zlog_warn("%s: Body Length of message greater than what we can read", + __func__); + return NULL; + } + + if (bodylen > 0) { + /* Read message body */ + rlen = readn(fd, buf, bodylen); + if (rlen < 0) { + zlog_warn("msg_read: readn %s", safe_strerror(errno)); + return NULL; + } else if (rlen == 0) { + zlog_warn("msg_read: Connection closed by peer"); + return NULL; + } else if (rlen != bodylen) { + zlog_warn("msg_read: Cannot read message body!"); + return NULL; + } + } + + /* Allocate new message */ + msg = msg_new(hdr.msgtype, buf, ntohl(hdr.msgseq), bodylen); + + return msg; +} + +int msg_write(int fd, struct msg *msg) +{ + uint8_t buf[OSPF_API_MAX_MSG_SIZE]; + uint16_t l; + int wlen; + + assert(msg); + assert(msg->s); + + /* Length of OSPF LSA payload */ + l = ntohs(msg->hdr.msglen); + if (l > OSPF_MAX_LSA_SIZE) { + zlog_warn("%s: wrong LSA size %d", __func__, l); + return -1; + } + + /* Make contiguous memory buffer for message */ + memcpy(buf, &msg->hdr, sizeof(struct apimsghdr)); + memcpy(buf + sizeof(struct apimsghdr), STREAM_DATA(msg->s), l); + + /* Total length of OSPF API Message */ + l += sizeof(struct apimsghdr); + wlen = writen(fd, buf, l); + if (wlen < 0) { + zlog_warn("%s: writen %s", __func__, safe_strerror(errno)); + return -1; + } else if (wlen == 0) { + zlog_warn("%s: Connection closed by peer", __func__); + return -1; + } else if (wlen != l) { + zlog_warn("%s: Cannot write API message", __func__); + return -1; + } + return 0; +} + +/* ----------------------------------------------------------- + * Specific messages + * ----------------------------------------------------------- + */ + +struct msg *new_msg_register_opaque_type(uint32_t seqnum, uint8_t ltype, + uint8_t otype) +{ + struct msg_register_opaque_type rmsg; + + rmsg.lsatype = ltype; + rmsg.opaquetype = otype; + memset(&rmsg.pad, 0, sizeof(rmsg.pad)); + + return msg_new(MSG_REGISTER_OPAQUETYPE, &rmsg, seqnum, + sizeof(struct msg_register_opaque_type)); +} + +struct msg *new_msg_register_event(uint32_t seqnum, + struct lsa_filter_type *filter) +{ + uint8_t buf[OSPF_API_MAX_MSG_SIZE]; + struct msg_register_event *emsg; + unsigned int len; + + emsg = (struct msg_register_event *)buf; + len = sizeof(struct msg_register_event) + + filter->num_areas * sizeof(struct in_addr); + emsg->filter.typemask = htons(filter->typemask); + emsg->filter.origin = filter->origin; + emsg->filter.num_areas = filter->num_areas; + if (len > sizeof(buf)) + len = sizeof(buf); + /* API broken - missing memcpy to fill data */ + return msg_new(MSG_REGISTER_EVENT, emsg, seqnum, len); +} + +struct msg *new_msg_sync_lsdb(uint32_t seqnum, struct lsa_filter_type *filter) +{ + uint8_t buf[OSPF_API_MAX_MSG_SIZE]; + struct msg_sync_lsdb *smsg; + unsigned int len; + + smsg = (struct msg_sync_lsdb *)buf; + len = sizeof(struct msg_sync_lsdb) + + filter->num_areas * sizeof(struct in_addr); + smsg->filter.typemask = htons(filter->typemask); + smsg->filter.origin = filter->origin; + smsg->filter.num_areas = filter->num_areas; + if (len > sizeof(buf)) + len = sizeof(buf); + /* API broken - missing memcpy to fill data */ + return msg_new(MSG_SYNC_LSDB, smsg, seqnum, len); +} + + +struct msg *new_msg_originate_request(uint32_t seqnum, struct in_addr ifaddr, + struct in_addr area_id, + struct lsa_header *data) +{ + struct msg_originate_request *omsg; + unsigned int omsglen; + char buf[OSPF_API_MAX_MSG_SIZE]; + size_t off_data = offsetof(struct msg_originate_request, data); + size_t data_maxs = sizeof(buf) - off_data; + struct lsa_header *omsg_data = (struct lsa_header *)&buf[off_data]; + + omsg = (struct msg_originate_request *)buf; + omsg->ifaddr = ifaddr; + omsg->area_id = area_id; + + omsglen = ntohs(data->length); + if (omsglen > data_maxs) + omsglen = data_maxs; + memcpy(omsg_data, data, omsglen); + omsglen += sizeof(struct msg_originate_request) + - sizeof(struct lsa_header); + + return msg_new(MSG_ORIGINATE_REQUEST, omsg, seqnum, omsglen); +} + +struct msg *new_msg_delete_request(uint32_t seqnum, struct in_addr addr, + uint8_t lsa_type, uint8_t opaque_type, + uint32_t opaque_id, uint8_t flags) +{ + struct msg_delete_request dmsg; + dmsg.addr = addr; + dmsg.lsa_type = lsa_type; + dmsg.opaque_type = opaque_type; + dmsg.opaque_id = htonl(opaque_id); + memset(&dmsg.pad, 0, sizeof(dmsg.pad)); + dmsg.flags = flags; + + return msg_new(MSG_DELETE_REQUEST, &dmsg, seqnum, + sizeof(struct msg_delete_request)); +} + + +struct msg *new_msg_reply(uint32_t seqnr, uint8_t rc) +{ + struct msg *msg; + struct msg_reply rmsg; + + /* Set return code */ + rmsg.errcode = rc; + memset(&rmsg.pad, 0, sizeof(rmsg.pad)); + + msg = msg_new(MSG_REPLY, &rmsg, seqnr, sizeof(struct msg_reply)); + + return msg; +} + +struct msg *new_msg_ready_notify(uint32_t seqnr, uint8_t lsa_type, + uint8_t opaque_type, struct in_addr addr) +{ + struct msg_ready_notify rmsg; + + rmsg.lsa_type = lsa_type; + rmsg.opaque_type = opaque_type; + memset(&rmsg.pad, 0, sizeof(rmsg.pad)); + rmsg.addr = addr; + + return msg_new(MSG_READY_NOTIFY, &rmsg, seqnr, + sizeof(struct msg_ready_notify)); +} + +struct msg *new_msg_new_if(uint32_t seqnr, struct in_addr ifaddr, + struct in_addr area_id) +{ + struct msg_new_if nmsg; + + nmsg.ifaddr = ifaddr; + nmsg.area_id = area_id; + + return msg_new(MSG_NEW_IF, &nmsg, seqnr, sizeof(struct msg_new_if)); +} + +struct msg *new_msg_del_if(uint32_t seqnr, struct in_addr ifaddr) +{ + struct msg_del_if dmsg; + + dmsg.ifaddr = ifaddr; + + return msg_new(MSG_DEL_IF, &dmsg, seqnr, sizeof(struct msg_del_if)); +} + +struct msg *new_msg_ism_change(uint32_t seqnr, struct in_addr ifaddr, + struct in_addr area_id, uint8_t status) +{ + struct msg_ism_change imsg; + + imsg.ifaddr = ifaddr; + imsg.area_id = area_id; + imsg.status = status; + memset(&imsg.pad, 0, sizeof(imsg.pad)); + + return msg_new(MSG_ISM_CHANGE, &imsg, seqnr, + sizeof(struct msg_ism_change)); +} + +struct msg *new_msg_nsm_change(uint32_t seqnr, struct in_addr ifaddr, + struct in_addr nbraddr, struct in_addr router_id, + uint8_t status) +{ + struct msg_nsm_change nmsg; + + nmsg.ifaddr = ifaddr; + nmsg.nbraddr = nbraddr; + nmsg.router_id = router_id; + nmsg.status = status; + memset(&nmsg.pad, 0, sizeof(nmsg.pad)); + + return msg_new(MSG_NSM_CHANGE, &nmsg, seqnr, + sizeof(struct msg_nsm_change)); +} + +struct msg *new_msg_lsa_change_notify(uint8_t msgtype, uint32_t seqnum, + struct in_addr ifaddr, + struct in_addr area_id, + uint8_t is_self_originated, + struct lsa_header *data) +{ + uint8_t buf[OSPF_API_MAX_MSG_SIZE]; + struct msg_lsa_change_notify *nmsg; + unsigned int len; + size_t off_data = offsetof(struct msg_lsa_change_notify, data); + size_t data_maxs = sizeof(buf) - off_data; + struct lsa_header *nmsg_data = (struct lsa_header *)&buf[off_data]; + + assert(data); + + nmsg = (struct msg_lsa_change_notify *)buf; + nmsg->ifaddr = ifaddr; + nmsg->area_id = area_id; + nmsg->is_self_originated = is_self_originated; + memset(&nmsg->pad, 0, sizeof(nmsg->pad)); + + len = ntohs(data->length); + if (len > data_maxs) + len = data_maxs; + memcpy(nmsg_data, data, len); + len += sizeof(struct msg_lsa_change_notify) - sizeof(struct lsa_header); + + return msg_new(msgtype, nmsg, seqnum, len); +} + +struct msg *new_msg_reachable_change(uint32_t seqnum, uint16_t nadd, + struct in_addr *add, uint16_t nremove, + struct in_addr *remove) +{ + uint8_t buf[OSPF_API_MAX_MSG_SIZE]; + struct msg_reachable_change *nmsg = (void *)buf; + const uint insz = sizeof(*nmsg->router_ids); + const uint nmax = (sizeof(buf) - sizeof(*nmsg)) / insz; + uint len; + + if (nadd > nmax) + nadd = nmax; + if (nremove > (nmax - nadd)) + nremove = (nmax - nadd); + + if (nadd) + memcpy(nmsg->router_ids, add, nadd * insz); + if (nremove) + memcpy(&nmsg->router_ids[nadd], remove, nremove * insz); + + nmsg->nadd = htons(nadd); + nmsg->nremove = htons(nremove); + len = sizeof(*nmsg) + insz * (nadd + nremove); + + return msg_new(MSG_REACHABLE_CHANGE, nmsg, seqnum, len); +} + +struct msg *new_msg_router_id_change(uint32_t seqnum, struct in_addr router_id) +{ + struct msg_router_id_change rmsg = {.router_id = router_id}; + + return msg_new(MSG_ROUTER_ID_CHANGE, &rmsg, seqnum, + sizeof(struct msg_router_id_change)); +} + +#endif /* SUPPORT_OSPF_API */ diff --git a/ospfd/ospf_api.h b/ospfd/ospf_api.h new file mode 100644 index 0000000..6c8bed3 --- /dev/null +++ b/ospfd/ospf_api.h @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * API message handling module for OSPF daemon and client. + * Copyright (C) 2001, 2002 Ralph Keller + * Copyright (c) 2022, LabN Consulting, L.L.C. + */ + + +/* This file is used both by the OSPFd and client applications to + define message formats used for communication. */ + +#ifndef _OSPF_API_H +#define _OSPF_API_H + +#include +#include "ospf_lsa.h" + +#define OSPF_API_VERSION 1 + +/* MTYPE definition is not reflected to "memory.h". */ +#define MTYPE_OSPF_API_MSG MTYPE_TMP +#define MTYPE_OSPF_API_FIFO MTYPE_TMP + +/* Default API server port to accept connection request from client-side. */ +/* This value could be overridden by "ospfapi" entry in "/etc/services". */ +#define OSPF_API_SYNC_PORT 2607 + +/* ----------------------------------------------------------- + * Generic messages + * ----------------------------------------------------------- + */ + +/* Message header structure, fields are in network byte order and + aligned to four octets. */ +struct apimsghdr { + uint8_t version; /* OSPF API protocol version */ + uint8_t msgtype; /* Type of message */ + uint16_t msglen; /* Length of message w/o header */ + uint32_t msgseq; /* Sequence number */ +}; + +/* Message representation with header and body */ +struct msg { + struct msg *next; /* to link into fifo */ + + /* Message header */ + struct apimsghdr hdr; + + /* Message body */ + struct stream *s; +}; + +/* Prototypes for generic messages. */ +extern struct msg *msg_new(uint8_t msgtype, void *msgbody, uint32_t seqnum, + uint16_t msglen); +extern struct msg *msg_dup(struct msg *msg); +extern void msg_print(struct msg *msg); /* XXX debug only */ +extern void msg_free(struct msg *msg); +struct msg *msg_read(int fd); +extern int msg_write(int fd, struct msg *msg); + +/* For requests, the message sequence number is between MIN_SEQ and + MAX_SEQ. For notifications, the sequence number is 0. */ + +#define MIN_SEQ 1 +#define MAX_SEQ 2147483647 + +extern void msg_set_seq(struct msg *msg, uint32_t seqnr); +extern uint32_t msg_get_seq(struct msg *msg); + +/* ----------------------------------------------------------- + * Message fifo queues + * ----------------------------------------------------------- + */ + +/* Message queue structure. */ +struct msg_fifo { + unsigned long count; + + struct msg *head; + struct msg *tail; +}; + +/* Prototype for message fifo queues. */ +extern struct msg_fifo *msg_fifo_new(void); +extern void msg_fifo_push(struct msg_fifo *, struct msg *msg); +extern struct msg *msg_fifo_pop(struct msg_fifo *fifo); +extern struct msg *msg_fifo_head(struct msg_fifo *fifo); +extern void msg_fifo_flush(struct msg_fifo *fifo); +extern void msg_fifo_free(struct msg_fifo *fifo); + +/* ----------------------------------------------------------- + * Specific message type and format definitions + * ----------------------------------------------------------- + */ + +/* Messages to OSPF daemon. */ +#define MSG_REGISTER_OPAQUETYPE 1 +#define MSG_UNREGISTER_OPAQUETYPE 2 +#define MSG_REGISTER_EVENT 3 +#define MSG_SYNC_LSDB 4 +#define MSG_ORIGINATE_REQUEST 5 +#define MSG_DELETE_REQUEST 6 +#define MSG_SYNC_REACHABLE 7 +#define MSG_SYNC_ISM 8 +#define MSG_SYNC_NSM 9 +#define MSG_SYNC_ROUTER_ID 19 + +/* Messages from OSPF daemon. */ +#define MSG_REPLY 10 +#define MSG_READY_NOTIFY 11 +#define MSG_LSA_UPDATE_NOTIFY 12 +#define MSG_LSA_DELETE_NOTIFY 13 +#define MSG_NEW_IF 14 +#define MSG_DEL_IF 15 +#define MSG_ISM_CHANGE 16 +#define MSG_NSM_CHANGE 17 +#define MSG_REACHABLE_CHANGE 18 +#define MSG_ROUTER_ID_CHANGE 20 + +struct msg_register_opaque_type { + uint8_t lsatype; + uint8_t opaquetype; + uint8_t pad[2]; /* padding */ +}; + +struct msg_unregister_opaque_type { + uint8_t lsatype; + uint8_t opaquetype; + uint8_t pad[2]; /* padding */ +}; + +/* Power2 is needed to convert LSA types into bit positions, + * see typemask below. Type definition starts at 1, so + * Power2[0] is not used. */ + + +static const uint16_t Power2[] = { + 0, (1 << 0), (1 << 1), (1 << 2), (1 << 3), (1 << 4), + (1 << 5), (1 << 6), (1 << 7), (1 << 8), (1 << 9), (1 << 10), + (1 << 11), (1 << 12), (1 << 13), (1 << 14), (1 << 15)}; + +struct lsa_filter_type { + uint16_t typemask; /* bitmask for selecting LSA types (1..16) */ + uint8_t origin; /* selects according to origin. */ +#define NON_SELF_ORIGINATED 0 +#define SELF_ORIGINATED (OSPF_LSA_SELF) +#define ANY_ORIGIN 2 + + uint8_t num_areas; /* number of areas in the filter. */ + /* areas, if any, go here. */ +}; + +struct msg_register_event { + struct lsa_filter_type filter; +}; + +struct msg_sync_lsdb { + struct lsa_filter_type filter; +}; + +struct msg_originate_request { + /* Used for LSA type 9 otherwise ignored */ + struct in_addr ifaddr; + + /* Used for LSA type 10 otherwise ignored */ + struct in_addr area_id; + + /* LSA header and LSA-specific part */ + struct lsa_header data; +}; + + +/* OSPF API MSG Delete Flag. */ +#define OSPF_API_DEL_ZERO_LEN_LSA 0x01 /* send withdrawal with no LSA data */ + +#define IS_DEL_ZERO_LEN_LSA(x) ((x)->flags & OSPF_API_DEL_ZERO_LEN_LSA) + +struct msg_delete_request { + struct in_addr addr; /* intf IP for link local, area for type 10, + "0.0.0.0" for AS-external */ + uint8_t lsa_type; + uint8_t opaque_type; + uint8_t pad; /* padding */ + uint8_t flags; /* delete flags */ + uint32_t opaque_id; +}; + +struct msg_reply { + signed char errcode; +#define OSPF_API_OK 0 +#define OSPF_API_NOSUCHINTERFACE (-1) +#define OSPF_API_NOSUCHAREA (-2) +#define OSPF_API_NOSUCHLSA (-3) +#define OSPF_API_ILLEGALLSATYPE (-4) +#define OSPF_API_OPAQUETYPEINUSE (-5) +#define OSPF_API_OPAQUETYPENOTREGISTERED (-6) +#define OSPF_API_NOTREADY (-7) +#define OSPF_API_NOMEMORY (-8) +#define OSPF_API_ERROR (-9) +#define OSPF_API_UNDEF (-10) + uint8_t pad[3]; /* padding to four byte alignment */ +}; + +/* Message to tell client application that it ospf daemon is + * ready to accept opaque LSAs for a given interface or area. */ + +struct msg_ready_notify { + uint8_t lsa_type; + uint8_t opaque_type; + uint8_t pad[2]; /* padding */ + struct in_addr addr; /* interface address or area address */ +}; + +/* These messages have a dynamic length depending on the embodied LSA. + They are aligned to four octets. msg_lsa_change_notify is used for + both LSA update and LSAs delete. */ + +struct msg_lsa_change_notify { + /* Used for LSA type 9 otherwise ignored */ + struct in_addr ifaddr; + /* Area ID. Not valid for AS-External and Opaque11 LSAs. */ + struct in_addr area_id; + uint8_t is_self_originated; /* 1 if self originated. */ + uint8_t pad[3]; + struct lsa_header data; +}; + +struct msg_new_if { + struct in_addr ifaddr; /* interface IP address */ + struct in_addr area_id; /* area this interface belongs to */ +}; + +struct msg_del_if { + struct in_addr ifaddr; /* interface IP address */ +}; + +struct msg_ism_change { + struct in_addr ifaddr; /* interface IP address */ + struct in_addr area_id; /* area this interface belongs to */ + uint8_t status; /* interface status (up/down) */ + uint8_t pad[3]; /* not used */ +}; + +struct msg_nsm_change { + struct in_addr ifaddr; /* attached interface */ + struct in_addr nbraddr; /* Neighbor interface address */ + struct in_addr router_id; /* Router ID of neighbor */ + uint8_t status; /* NSM status */ + uint8_t pad[3]; +}; + +struct msg_reachable_change { + uint16_t nadd; + uint16_t nremove; + struct in_addr router_ids[]; /* add followed by remove */ +}; + +struct msg_router_id_change { + struct in_addr router_id; /* this systems router id */ +}; + +/* We make use of a union to define a structure that covers all + possible API messages. This allows us to find out how much memory + needs to be reserved for the largest API message. */ +struct apimsg { + struct apimsghdr hdr; + union { + struct msg_register_opaque_type register_opaque_type; + struct msg_register_event register_event; + struct msg_sync_lsdb sync_lsdb; + struct msg_originate_request originate_request; + struct msg_delete_request delete_request; + struct msg_reply reply; + struct msg_ready_notify ready_notify; + struct msg_new_if new_if; + struct msg_del_if del_if; + struct msg_ism_change ism_change; + struct msg_nsm_change nsm_change; + struct msg_lsa_change_notify lsa_change_notify; + struct msg_reachable_change reachable_change; + struct msg_router_id_change router_id_change; + } u; +}; + +#define OSPF_API_MAX_MSG_SIZE (sizeof(struct apimsg) + OSPF_MAX_PACKET_SIZE) + +/* ----------------------------------------------------------- + * Prototypes for specific messages + * ----------------------------------------------------------- + */ + +/* For debugging only. */ +extern void api_opaque_lsa_print(struct ospf_lsa *lsa); + +/* Messages sent by client */ +extern struct msg *new_msg_register_opaque_type(uint32_t seqnum, uint8_t ltype, + uint8_t otype); +extern struct msg *new_msg_register_event(uint32_t seqnum, + struct lsa_filter_type *filter); +extern struct msg *new_msg_sync_lsdb(uint32_t seqnum, + struct lsa_filter_type *filter); +extern struct msg *new_msg_originate_request(uint32_t seqnum, + struct in_addr ifaddr, + struct in_addr area_id, + struct lsa_header *data); +extern struct msg *new_msg_delete_request(uint32_t seqnum, struct in_addr addr, + uint8_t lsa_type, uint8_t opaque_type, + uint32_t opaque_id, uint8_t flags); + +/* Messages sent by OSPF daemon */ +extern struct msg *new_msg_reply(uint32_t seqnum, uint8_t rc); + +extern struct msg *new_msg_ready_notify(uint32_t seqnr, uint8_t lsa_type, + uint8_t opaque_type, + struct in_addr addr); + +extern struct msg *new_msg_new_if(uint32_t seqnr, struct in_addr ifaddr, + struct in_addr area); + +extern struct msg *new_msg_del_if(uint32_t seqnr, struct in_addr ifaddr); + +extern struct msg *new_msg_ism_change(uint32_t seqnr, struct in_addr ifaddr, + struct in_addr area, uint8_t status); + +extern struct msg *new_msg_nsm_change(uint32_t seqnr, struct in_addr ifaddr, + struct in_addr nbraddr, + struct in_addr router_id, uint8_t status); + +/* msgtype is MSG_LSA_UPDATE_NOTIFY or MSG_LSA_DELETE_NOTIFY */ +extern struct msg *new_msg_lsa_change_notify(uint8_t msgtype, uint32_t seqnum, + struct in_addr ifaddr, + struct in_addr area_id, + uint8_t is_self_originated, + struct lsa_header *data); + +extern struct msg *new_msg_reachable_change(uint32_t seqnum, uint16_t nadd, + struct in_addr *add, + uint16_t nremove, + struct in_addr *remove); + +extern struct msg *new_msg_router_id_change(uint32_t seqnr, + struct in_addr router_id); +/* string printing functions */ +extern const char *ospf_api_errname(int errcode); +extern const char *ospf_api_typename(int msgtype); + +#endif /* _OSPF_API_H */ diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c new file mode 100644 index 0000000..34e59c5 --- /dev/null +++ b/ospfd/ospf_apiserver.c @@ -0,0 +1,2723 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Server side of OSPF API. + * Copyright (C) 2001, 2002 Ralph Keller + * Copyright (c) 2022, LabN Consulting, L.L.C. + */ + +#include + +#ifdef SUPPORT_OSPF_API + +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "memory.h" +#include "command.h" +#include "vty.h" +#include "stream.h" +#include "log.h" +#include "frrevent.h" +#include "hash.h" +#include "sockunion.h" /* for inet_aton() */ +#include "buffer.h" + +#include + +#include "ospfd/ospfd.h" /* for "struct event_loop" */ +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_errors.h" +#include "ospfd/ospf_memory.h" + +#include "ospfd/ospf_api.h" +#include "ospfd/ospf_apiserver.h" + +DEFINE_MTYPE_STATIC(OSPFD, APISERVER, "API Server"); +DEFINE_MTYPE_STATIC(OSPFD, APISERVER_MSGFILTER, "API Server Message Filter"); + +/* This is an implementation of an API to the OSPF daemon that allows + * external applications to access the OSPF daemon through socket + * connections. The application can use this API to inject its own + * opaque LSAs and flood them to other OSPF daemons. Other OSPF + * daemons then receive these LSAs and inform applications through the + * API by sending a corresponding message. The application can also + * register to receive all LSA types (in addition to opaque types) and + * use this information to reconstruct the OSPF's LSDB. The OSPF + * daemon supports multiple applications concurrently. */ + +/* List of all active connections. */ +struct list *apiserver_list; + +/* ----------------------------------------------------------- + * Functions to lookup interfaces + * ----------------------------------------------------------- + */ + +struct ospf_interface *ospf_apiserver_if_lookup_by_addr(struct in_addr address) +{ + struct listnode *node, *nnode; + struct ospf_interface *oi; + struct ospf *ospf = NULL; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (!ospf) + return NULL; + + for (ALL_LIST_ELEMENTS(ospf->oiflist, node, nnode, oi)) + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + if (IPV4_ADDR_SAME(&address, &oi->address->u.prefix4)) + return oi; + + return NULL; +} + +struct ospf_interface *ospf_apiserver_if_lookup_by_ifp(struct interface *ifp) +{ + struct listnode *node, *nnode; + struct ospf_interface *oi; + struct ospf *ospf = NULL; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (!ospf) + return NULL; + + for (ALL_LIST_ELEMENTS(ospf->oiflist, node, nnode, oi)) + if (oi->ifp == ifp) + return oi; + + return NULL; +} + +/* ----------------------------------------------------------- + * Initialization + * ----------------------------------------------------------- + */ + +unsigned short ospf_apiserver_getport(void) +{ + struct servent *sp = getservbyname("ospfapi", "tcp"); + + return sp ? ntohs(sp->s_port) : OSPF_API_SYNC_PORT; +} + +/* Initialize OSPF API module. Invoked from ospf_opaque_init() */ +int ospf_apiserver_init(void) +{ + int fd; + int rc = -1; + + /* Create new socket for synchronous messages. */ + fd = ospf_apiserver_serv_sock_family(ospf_apiserver_getport(), AF_INET); + + if (fd < 0) + goto out; + + /* Schedule new thread that handles accepted connections. */ + ospf_apiserver_event(OSPF_APISERVER_ACCEPT, fd, NULL); + + /* Initialize list that keeps track of all connections. */ + apiserver_list = list_new(); + + /* Register opaque-independent call back functions. These functions + are invoked on ISM, NSM changes and LSA update and LSA deletes */ + rc = ospf_register_opaque_functab( + 0 /* all LSAs */, 0 /* all opaque types */, + ospf_apiserver_new_if, ospf_apiserver_del_if, + ospf_apiserver_ism_change, ospf_apiserver_nsm_change, NULL, + NULL, NULL, NULL, /* ospf_apiserver_show_info */ + NULL, /* originator_func */ + NULL, /* ospf_apiserver_lsa_refresher */ + ospf_apiserver_lsa_update, ospf_apiserver_lsa_delete); + if (rc != 0) { + flog_warn( + EC_OSPF_OPAQUE_REGISTRATION, + "ospf_apiserver_init: Failed to register opaque type [0/0]"); + } + + rc = 0; + +out: + return rc; +} + +/* Terminate OSPF API module. */ +void ospf_apiserver_term(void) +{ + struct ospf_apiserver *apiserv; + + /* Unregister wildcard [0/0] type */ + ospf_delete_opaque_functab(0 /* all LSAs */, 0 /* all opaque types */); + + /* + * Free all client instances. ospf_apiserver_free removes the node + * from the list, so we examine the head of the list anew each time. + */ + if (!apiserver_list) + return; + + while (listcount(apiserver_list)) { + apiserv = listgetdata(listhead(apiserver_list)); + + ospf_apiserver_free(apiserv); + } + + /* Free client list itself */ + if (apiserver_list) + list_delete(&apiserver_list); + + /* Free wildcard list */ + /* XXX */ +} + +static struct ospf_apiserver *lookup_apiserver(uint8_t lsa_type, + uint8_t opaque_type) +{ + struct listnode *n1, *n2; + struct registered_opaque_type *r; + struct ospf_apiserver *apiserv, *found = NULL; + + /* XXX: this approaches O(n**2) */ + for (ALL_LIST_ELEMENTS_RO(apiserver_list, n1, apiserv)) { + for (ALL_LIST_ELEMENTS_RO(apiserv->opaque_types, n2, r)) + if (r->lsa_type == lsa_type + && r->opaque_type == opaque_type) { + found = apiserv; + goto out; + } + } +out: + return found; +} + +static struct ospf_apiserver *lookup_apiserver_by_lsa(struct ospf_lsa *lsa) +{ + struct lsa_header *lsah = lsa->data; + struct ospf_apiserver *found = NULL; + + if (IS_OPAQUE_LSA(lsah->type)) { + found = lookup_apiserver( + lsah->type, GET_OPAQUE_TYPE(ntohl(lsah->id.s_addr))); + } + return found; +} + +/* ----------------------------------------------------------- + * Following are functions to manage client connections. + * ----------------------------------------------------------- + */ +static int ospf_apiserver_new_lsa_hook(struct ospf_lsa *lsa) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("API: Put LSA(%p)[%s] into reserve, total=%ld", + (void *)lsa, dump_lsa_key(lsa), lsa->lsdb->total); + return 0; +} + +static int ospf_apiserver_del_lsa_hook(struct ospf_lsa *lsa) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("API: Get LSA(%p)[%s] from reserve, total=%ld", + (void *)lsa, dump_lsa_key(lsa), lsa->lsdb->total); + return 0; +} + +/* Allocate new connection structure. */ +struct ospf_apiserver *ospf_apiserver_new(int fd_sync, int fd_async) +{ + struct ospf_apiserver *new = + XMALLOC(MTYPE_APISERVER, sizeof(struct ospf_apiserver)); + + new->filter = XMALLOC(MTYPE_APISERVER_MSGFILTER, + sizeof(struct lsa_filter_type)); + + new->fd_sync = fd_sync; + new->fd_async = fd_async; + + /* list of registered opaque types that application uses */ + new->opaque_types = list_new(); + + /* Initialize temporary strage for LSA instances to be refreshed. */ + memset(&new->reserve, 0, sizeof(struct ospf_lsdb)); + ospf_lsdb_init(&new->reserve); + + new->reserve.new_lsa_hook = ospf_apiserver_new_lsa_hook; /* debug */ + new->reserve.del_lsa_hook = ospf_apiserver_del_lsa_hook; /* debug */ + + new->out_sync_fifo = msg_fifo_new(); + new->out_async_fifo = msg_fifo_new(); + new->t_sync_read = NULL; +#ifdef USE_ASYNC_READ + new->t_async_read = NULL; +#endif /* USE_ASYNC_READ */ + new->t_sync_write = NULL; + new->t_async_write = NULL; + + new->filter->typemask = 0; /* filter all LSAs */ + new->filter->origin = ANY_ORIGIN; + new->filter->num_areas = 0; + + return new; +} + +void ospf_apiserver_event(enum ospf_apiserver_event event, int fd, + struct ospf_apiserver *apiserv) +{ + switch (event) { + case OSPF_APISERVER_ACCEPT: + (void)event_add_read(master, ospf_apiserver_accept, apiserv, fd, + NULL); + break; + case OSPF_APISERVER_SYNC_READ: + apiserv->t_sync_read = NULL; + event_add_read(master, ospf_apiserver_read, apiserv, fd, + &apiserv->t_sync_read); + break; +#ifdef USE_ASYNC_READ + case OSPF_APISERVER_ASYNC_READ: + apiserv->t_async_read = NULL; + event_add_read(master, ospf_apiserver_read, apiserv, fd, + &apiserv->t_async_read); + break; +#endif /* USE_ASYNC_READ */ + case OSPF_APISERVER_SYNC_WRITE: + event_add_write(master, ospf_apiserver_sync_write, apiserv, fd, + &apiserv->t_sync_write); + break; + case OSPF_APISERVER_ASYNC_WRITE: + event_add_write(master, ospf_apiserver_async_write, apiserv, fd, + &apiserv->t_async_write); + break; + } +} + +/* Free instance. First unregister all opaque types used by + application, flush opaque LSAs injected by application + from network and close connection. */ +void ospf_apiserver_free(struct ospf_apiserver *apiserv) +{ + struct listnode *node; + + /* Cancel read and write threads. */ + EVENT_OFF(apiserv->t_sync_read); +#ifdef USE_ASYNC_READ + EVENT_OFF(apiserv->t_async_read); +#endif /* USE_ASYNC_READ */ + EVENT_OFF(apiserv->t_sync_write); + EVENT_OFF(apiserv->t_async_write); + + /* Unregister all opaque types that application registered + and flush opaque LSAs if still in LSDB. */ + + while ((node = listhead(apiserv->opaque_types)) != NULL) { + struct registered_opaque_type *regtype = listgetdata(node); + + ospf_apiserver_unregister_opaque_type( + apiserv, regtype->lsa_type, regtype->opaque_type); + } + list_delete(&apiserv->opaque_types); + + /* Close connections to OSPFd. */ + if (apiserv->fd_sync > 0) { + close(apiserv->fd_sync); + } + + if (apiserv->fd_async > 0) { + close(apiserv->fd_async); + } + + /* Free fifos */ + msg_fifo_free(apiserv->out_sync_fifo); + msg_fifo_free(apiserv->out_async_fifo); + + /* Clear temporary strage for LSA instances to be refreshed. */ + ospf_lsdb_delete_all(&apiserv->reserve); + ospf_lsdb_cleanup(&apiserv->reserve); + + /* Remove from the list of active clients. */ + listnode_delete(apiserver_list, apiserv); + + XFREE(MTYPE_APISERVER_MSGFILTER, apiserv->filter); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("API: Delete apiserv(%p), total#(%d)", + (void *)apiserv, apiserver_list->count); + + /* And free instance. */ + XFREE(MTYPE_APISERVER, apiserv); +} + +void ospf_apiserver_read(struct event *thread) +{ + struct ospf_apiserver *apiserv; + struct msg *msg; + int fd; + enum ospf_apiserver_event event; + + apiserv = EVENT_ARG(thread); + fd = EVENT_FD(thread); + + if (fd == apiserv->fd_sync) { + event = OSPF_APISERVER_SYNC_READ; + apiserv->t_sync_read = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("API: %s: Peer: %pI4/%u", __func__, + &apiserv->peer_sync.sin_addr, + ntohs(apiserv->peer_sync.sin_port)); + } +#ifdef USE_ASYNC_READ + else if (fd == apiserv->fd_async) { + event = OSPF_APISERVER_ASYNC_READ; + apiserv->t_async_read = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("API: %s: Peer: %pI4/%u", __func__, + &apiserv->peer_async.sin_addr, + ntohs(apiserv->peer_async.sin_port)); + } +#endif /* USE_ASYNC_READ */ + else { + zlog_warn("%s: Unknown fd(%d)", __func__, fd); + ospf_apiserver_free(apiserv); + return; + } + + /* Read message from fd. */ + msg = msg_read(fd); + if (msg == NULL) { + zlog_warn("%s: read failed on fd=%d, closing connection", + __func__, fd); + + /* Perform cleanup. */ + ospf_apiserver_free(apiserv); + return; + } + + if (IS_DEBUG_OSPF_EVENT) + msg_print(msg); + + /* Dispatch to corresponding message handler. */ + ospf_apiserver_handle_msg(apiserv, msg); + + /* Prepare for next message, add read thread. */ + ospf_apiserver_event(event, fd, apiserv); + + msg_free(msg); +} + +void ospf_apiserver_sync_write(struct event *thread) +{ + struct ospf_apiserver *apiserv; + struct msg *msg; + int fd; + int rc = -1; + + apiserv = EVENT_ARG(thread); + assert(apiserv); + fd = EVENT_FD(thread); + + apiserv->t_sync_write = NULL; + + /* Sanity check */ + if (fd != apiserv->fd_sync) { + zlog_warn("%s: Unknown fd=%d", __func__, fd); + goto out; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("API: %s: Peer: %pI4/%u", __func__, + &apiserv->peer_sync.sin_addr, + ntohs(apiserv->peer_sync.sin_port)); + + /* Check whether there is really a message in the fifo. */ + msg = msg_fifo_pop(apiserv->out_sync_fifo); + if (!msg) { + zlog_warn("API: %s: No message in Sync-FIFO?", __func__); + return; + } + + if (IS_DEBUG_OSPF_EVENT) + msg_print(msg); + + rc = msg_write(fd, msg); + + /* Once a message is dequeued, it should be freed anyway. */ + msg_free(msg); + + if (rc < 0) { + zlog_warn("%s: write failed on fd=%d", __func__, fd); + goto out; + } + + + /* If more messages are in sync message fifo, schedule write thread. */ + if (msg_fifo_head(apiserv->out_sync_fifo)) { + ospf_apiserver_event(OSPF_APISERVER_SYNC_WRITE, + apiserv->fd_sync, apiserv); + } + +out: + + if (rc < 0) { + /* Perform cleanup and disconnect with peer */ + ospf_apiserver_free(apiserv); + } +} + + +void ospf_apiserver_async_write(struct event *thread) +{ + struct ospf_apiserver *apiserv; + struct msg *msg; + int fd; + int rc = -1; + + apiserv = EVENT_ARG(thread); + assert(apiserv); + fd = EVENT_FD(thread); + + apiserv->t_async_write = NULL; + + /* Sanity check */ + if (fd != apiserv->fd_async) { + zlog_warn("%s: Unknown fd=%d", __func__, fd); + goto out; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("API: %s: Peer: %pI4/%u", __func__, + &apiserv->peer_async.sin_addr, + ntohs(apiserv->peer_async.sin_port)); + + /* Check whether there is really a message in the fifo. */ + msg = msg_fifo_pop(apiserv->out_async_fifo); + if (!msg) { + zlog_warn("API: %s: No message in Async-FIFO?", __func__); + return; + } + + if (IS_DEBUG_OSPF_EVENT) + msg_print(msg); + + rc = msg_write(fd, msg); + + /* Once a message is dequeued, it should be freed anyway. */ + msg_free(msg); + + if (rc < 0) { + zlog_warn("%s: write failed on fd=%d", __func__, fd); + goto out; + } + + + /* If more messages are in async message fifo, schedule write thread. */ + if (msg_fifo_head(apiserv->out_async_fifo)) { + ospf_apiserver_event(OSPF_APISERVER_ASYNC_WRITE, + apiserv->fd_async, apiserv); + } + +out: + + if (rc < 0) { + /* Perform cleanup and disconnect with peer */ + ospf_apiserver_free(apiserv); + } +} + + +int ospf_apiserver_serv_sock_family(unsigned short port, int family) +{ + union sockunion su; + int accept_sock; + int rc; + + memset(&su, 0, sizeof(union sockunion)); + su.sa.sa_family = family; + + /* Make new socket */ + accept_sock = sockunion_stream_socket(&su); + if (accept_sock < 0) + return accept_sock; + + /* This is a server, so reuse address and port */ + sockopt_reuseaddr(accept_sock); + sockopt_reuseport(accept_sock); + + /* Bind socket to address and given port. */ + rc = sockunion_bind(accept_sock, &su, port, NULL); + if (rc < 0) { + close(accept_sock); /* Close socket */ + return rc; + } + + /* Listen socket under queue length 3. */ + rc = listen(accept_sock, 3); + if (rc < 0) { + zlog_warn("%s: listen: %s", __func__, safe_strerror(errno)); + close(accept_sock); /* Close socket */ + return rc; + } + return accept_sock; +} + + +/* Accept connection request from external applications. For each + accepted connection allocate own connection instance. */ +void ospf_apiserver_accept(struct event *thread) +{ + int accept_sock; + int new_sync_sock; + int new_async_sock; + union sockunion su; + struct ospf_apiserver *apiserv; + struct sockaddr_in peer_async; + struct sockaddr_in peer_sync; + unsigned int peerlen; + int ret; + + /* EVENT_ARG (thread) is NULL */ + accept_sock = EVENT_FD(thread); + + /* Keep hearing on socket for further connections. */ + ospf_apiserver_event(OSPF_APISERVER_ACCEPT, accept_sock, NULL); + + memset(&su, 0, sizeof(union sockunion)); + /* Accept connection for synchronous messages */ + new_sync_sock = sockunion_accept(accept_sock, &su); + if (new_sync_sock < 0) { + zlog_warn("%s: accept: %s", __func__, safe_strerror(errno)); + return; + } + + /* Get port address and port number of peer to make reverse connection. + The reverse channel uses the port number of the peer port+1. */ + + memset(&peer_sync, 0, sizeof(peer_sync)); + peerlen = sizeof(struct sockaddr_in); + + ret = getpeername(new_sync_sock, (struct sockaddr *)&peer_sync, + &peerlen); + if (ret < 0) { + zlog_warn("%s: getpeername: %s", __func__, + safe_strerror(errno)); + close(new_sync_sock); + return; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("API: %s: New peer: %pI4/%u", __func__, + &peer_sync.sin_addr, ntohs(peer_sync.sin_port)); + + /* Create new socket for asynchronous messages. */ + peer_async = peer_sync; + peer_async.sin_port = htons(ntohs(peer_sync.sin_port) + 1); + + /* Check if remote port number to make reverse connection is valid one. + */ + if (ntohs(peer_async.sin_port) == ospf_apiserver_getport()) { + zlog_warn("API: %s: Peer(%pI4/%u): Invalid async port number?", + __func__, &peer_async.sin_addr, + ntohs(peer_async.sin_port)); + close(new_sync_sock); + return; + } + + new_async_sock = socket(AF_INET, SOCK_STREAM, 0); + if (new_async_sock < 0) { + zlog_warn("%s: socket: %s", __func__, safe_strerror(errno)); + close(new_sync_sock); + return; + } + + ret = connect(new_async_sock, (struct sockaddr *)&peer_async, + sizeof(struct sockaddr_in)); + + if (ret < 0) { + zlog_warn("%s: connect: %s", __func__, safe_strerror(errno)); + close(new_sync_sock); + close(new_async_sock); + return; + } + +#ifdef USE_ASYNC_READ +#else /* USE_ASYNC_READ */ + /* Make the asynchronous channel write-only. */ + ret = shutdown(new_async_sock, SHUT_RD); + if (ret < 0) { + zlog_warn("%s: shutdown: %s", __func__, safe_strerror(errno)); + close(new_sync_sock); + close(new_async_sock); + return; + } +#endif /* USE_ASYNC_READ */ + + /* Allocate new server-side connection structure */ + apiserv = ospf_apiserver_new(new_sync_sock, new_async_sock); + + /* Add to active connection list */ + listnode_add(apiserver_list, apiserv); + apiserv->peer_sync = peer_sync; + apiserv->peer_async = peer_async; + + /* And add read threads for new connection */ + ospf_apiserver_event(OSPF_APISERVER_SYNC_READ, new_sync_sock, apiserv); +#ifdef USE_ASYNC_READ + ospf_apiserver_event(OSPF_APISERVER_ASYNC_READ, new_async_sock, + apiserv); +#endif /* USE_ASYNC_READ */ + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("API: New apiserv(%p), total#(%d)", (void *)apiserv, + apiserver_list->count); +} + + +/* ----------------------------------------------------------- + * Send reply with return code to client application + * ----------------------------------------------------------- + */ + +static int ospf_apiserver_send_msg(struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct msg_fifo *fifo; + struct msg *msg2; + enum ospf_apiserver_event event; + int fd; + + switch (msg->hdr.msgtype) { + case MSG_REPLY: + fifo = apiserv->out_sync_fifo; + fd = apiserv->fd_sync; + event = OSPF_APISERVER_SYNC_WRITE; + break; + case MSG_READY_NOTIFY: + case MSG_LSA_UPDATE_NOTIFY: + case MSG_LSA_DELETE_NOTIFY: + case MSG_NEW_IF: + case MSG_DEL_IF: + case MSG_ISM_CHANGE: + case MSG_NSM_CHANGE: + case MSG_REACHABLE_CHANGE: + case MSG_ROUTER_ID_CHANGE: + fifo = apiserv->out_async_fifo; + fd = apiserv->fd_async; + event = OSPF_APISERVER_ASYNC_WRITE; + break; + default: + zlog_warn("%s: Unknown message type %d", __func__, + msg->hdr.msgtype); + return -1; + } + + /* Make a copy of the message and put in the fifo. Once the fifo + gets drained by the write thread, the message will be freed. */ + /* NB: Given "msg" is untouched in this function. */ + msg2 = msg_dup(msg); + + /* Enqueue message into corresponding fifo queue */ + msg_fifo_push(fifo, msg2); + + /* Schedule write thread */ + ospf_apiserver_event(event, fd, apiserv); + return 0; +} + +int ospf_apiserver_send_reply(struct ospf_apiserver *apiserv, uint32_t seqnr, + uint8_t rc) +{ + struct msg *msg = new_msg_reply(seqnr, rc); + int ret; + + if (!msg) { + zlog_warn("%s: msg_new failed", __func__); +#ifdef NOTYET + /* Cannot allocate new message. What should we do? */ + ospf_apiserver_free(apiserv); +#endif + return -1; + } + + ret = ospf_apiserver_send_msg(apiserv, msg); + msg_free(msg); + return ret; +} + + +/* ----------------------------------------------------------- + * Generic message dispatching handler function + * ----------------------------------------------------------- + */ + +int ospf_apiserver_handle_msg(struct ospf_apiserver *apiserv, struct msg *msg) +{ + int rc; + + /* Call corresponding message handler function. */ + switch (msg->hdr.msgtype) { + case MSG_REGISTER_OPAQUETYPE: + rc = ospf_apiserver_handle_register_opaque_type(apiserv, msg); + break; + case MSG_UNREGISTER_OPAQUETYPE: + rc = ospf_apiserver_handle_unregister_opaque_type(apiserv, msg); + break; + case MSG_REGISTER_EVENT: + rc = ospf_apiserver_handle_register_event(apiserv, msg); + break; + case MSG_SYNC_LSDB: + rc = ospf_apiserver_handle_sync_lsdb(apiserv, msg); + break; + case MSG_ORIGINATE_REQUEST: + rc = ospf_apiserver_handle_originate_request(apiserv, msg); + break; + case MSG_DELETE_REQUEST: + rc = ospf_apiserver_handle_delete_request(apiserv, msg); + break; + case MSG_SYNC_REACHABLE: + rc = ospf_apiserver_handle_sync_reachable(apiserv, msg); + break; + case MSG_SYNC_ISM: + rc = ospf_apiserver_handle_sync_ism(apiserv, msg); + break; + case MSG_SYNC_NSM: + rc = ospf_apiserver_handle_sync_nsm(apiserv, msg); + break; + case MSG_SYNC_ROUTER_ID: + rc = ospf_apiserver_handle_sync_router_id(apiserv, msg); + break; + default: + zlog_warn("%s: Unknown message type: %d", __func__, + msg->hdr.msgtype); + rc = -1; + } + return rc; +} + + +/* ----------------------------------------------------------- + * Following are functions for opaque type registration + * ----------------------------------------------------------- + */ + +int ospf_apiserver_register_opaque_type(struct ospf_apiserver *apiserv, + uint8_t lsa_type, uint8_t opaque_type) +{ + struct registered_opaque_type *regtype; + int (*originator_func)(void *arg); + int rc; + + switch (lsa_type) { + case OSPF_OPAQUE_LINK_LSA: + originator_func = ospf_apiserver_lsa9_originator; + break; + case OSPF_OPAQUE_AREA_LSA: + originator_func = ospf_apiserver_lsa10_originator; + break; + case OSPF_OPAQUE_AS_LSA: + originator_func = ospf_apiserver_lsa11_originator; + break; + default: + zlog_warn("%s: lsa_type(%d)", __func__, lsa_type); + return OSPF_API_ILLEGALLSATYPE; + } + + + /* Register opaque function table */ + /* NB: Duplicated registration will be detected inside the function. */ + rc = ospf_register_opaque_functab( + lsa_type, opaque_type, NULL, /* ospf_apiserver_new_if */ + NULL, /* ospf_apiserver_del_if */ + NULL, /* ospf_apiserver_ism_change */ + NULL, /* ospf_apiserver_nsm_change */ + NULL, NULL, NULL, ospf_apiserver_show_info, originator_func, + ospf_apiserver_lsa_refresher, + NULL, /* ospf_apiserver_lsa_update */ + NULL /* ospf_apiserver_lsa_delete */); + + if (rc != 0) { + flog_warn(EC_OSPF_OPAQUE_REGISTRATION, + "Failed to register opaque type [%d/%d]", lsa_type, + opaque_type); + return OSPF_API_OPAQUETYPEINUSE; + } + + /* Remember the opaque type that application registers so when + connection shuts down, we can flush all LSAs of this opaque + type. */ + + regtype = + XCALLOC(MTYPE_APISERVER, sizeof(struct registered_opaque_type)); + regtype->lsa_type = lsa_type; + regtype->opaque_type = opaque_type; + + /* Add to list of registered opaque types */ + listnode_add(apiserv->opaque_types, regtype); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "API: Add LSA-type(%d)/Opaque-type(%d) into apiserv(%p), total#(%d)", + lsa_type, opaque_type, (void *)apiserv, + listcount(apiserv->opaque_types)); + + return 0; +} + +int ospf_apiserver_unregister_opaque_type(struct ospf_apiserver *apiserv, + uint8_t lsa_type, uint8_t opaque_type) +{ + struct listnode *node, *nnode; + struct registered_opaque_type *regtype; + + for (ALL_LIST_ELEMENTS(apiserv->opaque_types, node, nnode, regtype)) { + /* Check if we really registered this opaque type */ + if (regtype->lsa_type == lsa_type + && regtype->opaque_type == opaque_type) { + + /* Yes, we registered this opaque type. Flush + all existing opaque LSAs of this type */ + + ospf_apiserver_flush_opaque_lsa(apiserv, lsa_type, + opaque_type); + ospf_delete_opaque_functab(lsa_type, opaque_type); + + /* Remove from list of registered opaque types */ + listnode_delete(apiserv->opaque_types, regtype); + + XFREE(MTYPE_APISERVER, regtype); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "API: Del LSA-type(%d)/Opaque-type(%d) from apiserv(%p), total#(%d)", + lsa_type, opaque_type, (void *)apiserv, + listcount(apiserv->opaque_types)); + + return 0; + } + } + + /* Opaque type is not registered */ + zlog_warn("Failed to unregister opaque type [%d/%d]", lsa_type, + opaque_type); + return OSPF_API_OPAQUETYPENOTREGISTERED; +} + + +static int apiserver_is_opaque_type_registered(struct ospf_apiserver *apiserv, + uint8_t lsa_type, + uint8_t opaque_type) +{ + struct listnode *node, *nnode; + struct registered_opaque_type *regtype; + + /* XXX: how many types are there? if few, why not just a bitmap? */ + for (ALL_LIST_ELEMENTS(apiserv->opaque_types, node, nnode, regtype)) { + /* Check if we really registered this opaque type */ + if (regtype->lsa_type == lsa_type + && regtype->opaque_type == opaque_type) { + /* Yes registered */ + return 1; + } + } + /* Not registered */ + return 0; +} + +int ospf_apiserver_handle_register_opaque_type(struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct msg_register_opaque_type *rmsg; + uint8_t lsa_type; + uint8_t opaque_type; + int rc = 0; + + /* Extract parameters from register opaque type message */ + rmsg = (struct msg_register_opaque_type *)STREAM_DATA(msg->s); + + lsa_type = rmsg->lsatype; + opaque_type = rmsg->opaquetype; + + rc = ospf_apiserver_register_opaque_type(apiserv, lsa_type, + opaque_type); + + /* Send a reply back to client including return code */ + rc = ospf_apiserver_send_reply(apiserv, ntohl(msg->hdr.msgseq), rc); + if (rc < 0) + goto out; + + /* Now inform application about opaque types that are ready */ + switch (lsa_type) { + case OSPF_OPAQUE_LINK_LSA: + ospf_apiserver_notify_ready_type9(apiserv); + break; + case OSPF_OPAQUE_AREA_LSA: + ospf_apiserver_notify_ready_type10(apiserv); + break; + case OSPF_OPAQUE_AS_LSA: + ospf_apiserver_notify_ready_type11(apiserv); + break; + } +out: + return rc; +} + + +/* Notify specific client about all opaque types 9 that are ready. */ +void ospf_apiserver_notify_ready_type9(struct ospf_apiserver *apiserv) +{ + struct listnode *node, *nnode; + struct listnode *node2, *nnode2; + struct ospf *ospf; + struct ospf_interface *oi; + struct registered_opaque_type *r; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + for (ALL_LIST_ELEMENTS(ospf->oiflist, node, nnode, oi)) { + /* Check if this interface is indeed ready for type 9 */ + if (!ospf_apiserver_is_ready_type9(oi)) + continue; + + /* Check for registered opaque type 9 types */ + /* XXX: loop-de-loop - optimise me */ + for (ALL_LIST_ELEMENTS(apiserv->opaque_types, node2, nnode2, + r)) { + struct msg *msg; + + if (r->lsa_type == OSPF_OPAQUE_LINK_LSA) { + + /* Yes, this opaque type is ready */ + msg = new_msg_ready_notify( + 0, OSPF_OPAQUE_LINK_LSA, r->opaque_type, + oi->address->u.prefix4); + if (!msg) { + zlog_warn("%s: msg_new failed", + __func__); +#ifdef NOTYET + /* Cannot allocate new message. What + * should we do? */ + ospf_apiserver_free(apiserv); +#endif + goto out; + } + ospf_apiserver_send_msg(apiserv, msg); + msg_free(msg); + } + } + } + +out: + return; +} + + +/* Notify specific client about all opaque types 10 that are ready. */ +void ospf_apiserver_notify_ready_type10(struct ospf_apiserver *apiserv) +{ + struct listnode *node, *nnode; + struct listnode *node2, *nnode2; + struct ospf *ospf; + struct ospf_area *area; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + struct registered_opaque_type *r; + + if (!ospf_apiserver_is_ready_type10(area)) { + continue; + } + + /* Check for registered opaque type 10 types */ + /* XXX: loop in loop - optimise me */ + for (ALL_LIST_ELEMENTS(apiserv->opaque_types, node2, nnode2, + r)) { + struct msg *msg; + + if (r->lsa_type == OSPF_OPAQUE_AREA_LSA) { + /* Yes, this opaque type is ready */ + msg = new_msg_ready_notify( + 0, OSPF_OPAQUE_AREA_LSA, r->opaque_type, + area->area_id); + if (!msg) { + zlog_warn("%s: msg_new failed", + __func__); +#ifdef NOTYET + /* Cannot allocate new message. What + * should we do? */ + ospf_apiserver_free(apiserv); +#endif + goto out; + } + ospf_apiserver_send_msg(apiserv, msg); + msg_free(msg); + } + } + } + +out: + return; +} + +/* Notify specific client about all opaque types 11 that are ready */ +void ospf_apiserver_notify_ready_type11(struct ospf_apiserver *apiserv) +{ + struct listnode *node, *nnode; + struct ospf *ospf; + struct registered_opaque_type *r; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + /* Can type 11 be originated? */ + if (!ospf_apiserver_is_ready_type11(ospf)) + goto out; + + /* Check for registered opaque type 11 types */ + for (ALL_LIST_ELEMENTS(apiserv->opaque_types, node, nnode, r)) { + struct msg *msg; + struct in_addr noarea_id = {.s_addr = 0L}; + + if (r->lsa_type == OSPF_OPAQUE_AS_LSA) { + /* Yes, this opaque type is ready */ + msg = new_msg_ready_notify(0, OSPF_OPAQUE_AS_LSA, + r->opaque_type, noarea_id); + + if (!msg) { + zlog_warn("%s: msg_new failed", __func__); +#ifdef NOTYET + /* Cannot allocate new message. What should we + * do? */ + ospf_apiserver_free(apiserv); +#endif + goto out; + } + ospf_apiserver_send_msg(apiserv, msg); + msg_free(msg); + } + } + +out: + return; +} + +int ospf_apiserver_handle_unregister_opaque_type(struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct msg_unregister_opaque_type *umsg; + uint8_t ltype; + uint8_t otype; + int rc = 0; + + /* Extract parameters from unregister opaque type message */ + umsg = (struct msg_unregister_opaque_type *)STREAM_DATA(msg->s); + + ltype = umsg->lsatype; + otype = umsg->opaquetype; + + rc = ospf_apiserver_unregister_opaque_type(apiserv, ltype, otype); + + /* Send a reply back to client including return code */ + rc = ospf_apiserver_send_reply(apiserv, ntohl(msg->hdr.msgseq), rc); + + return rc; +} + + +/* ----------------------------------------------------------- + * Following are functions for event (filter) registration. + * ----------------------------------------------------------- + */ +int ospf_apiserver_handle_register_event(struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct msg_register_event *rmsg; + int rc; + uint32_t seqnum; + size_t size; + + rmsg = (struct msg_register_event *)STREAM_DATA(msg->s); + + /* Get request sequence number */ + seqnum = msg_get_seq(msg); + + /* Free existing filter in apiserv. */ + XFREE(MTYPE_APISERVER_MSGFILTER, apiserv->filter); + /* Alloc new space for filter. */ + size = ntohs(msg->hdr.msglen); + if (size < OSPF_MAX_LSA_SIZE) { + + apiserv->filter = XMALLOC(MTYPE_APISERVER_MSGFILTER, size); + + /* copy it over. */ + memcpy(apiserv->filter, &rmsg->filter, size); + rc = OSPF_API_OK; + } else + rc = OSPF_API_NOMEMORY; + + /* Send a reply back to client with return code */ + rc = ospf_apiserver_send_reply(apiserv, seqnum, rc); + return rc; +} + + +/* ----------------------------------------------------------- + * Following are functions for LSDB synchronization. + * ----------------------------------------------------------- + */ + +static int apiserver_sync_callback(struct ospf_lsa *lsa, void *p_arg, + int int_arg) +{ + struct ospf_apiserver *apiserv; + int seqnum; + struct msg *msg; + struct param_t { + struct ospf_apiserver *apiserv; + struct lsa_filter_type *filter; + } * param; + int rc = -1; + + /* Sanity check */ + assert(lsa->data); + assert(p_arg); + + param = (struct param_t *)p_arg; + apiserv = param->apiserv; + seqnum = (uint32_t)int_arg; + + /* Check origin in filter. */ + if ((param->filter->origin == ANY_ORIGIN) + || (param->filter->origin == (lsa->flags & OSPF_LSA_SELF))) { + + /* Default area for AS-External and Opaque11 LSAs */ + struct in_addr area_id = {.s_addr = 0L}; + + /* Default interface for non Opaque9 LSAs */ + struct in_addr ifaddr = {.s_addr = 0L}; + + if (lsa->area) { + area_id = lsa->area->area_id; + } + if (lsa->data->type == OSPF_OPAQUE_LINK_LSA) { + ifaddr = lsa->oi->address->u.prefix4; + } + + msg = new_msg_lsa_change_notify( + MSG_LSA_UPDATE_NOTIFY, seqnum, ifaddr, area_id, + lsa->flags & OSPF_LSA_SELF, lsa->data); + if (!msg) { + zlog_warn("%s: new_msg_update failed", __func__); +#ifdef NOTYET + /* Cannot allocate new message. What should we do? */ + /* ospf_apiserver_free (apiserv);*/ /* Do nothing + here XXX + */ +#endif + goto out; + } + + /* Send LSA */ + ospf_apiserver_send_msg(apiserv, msg); + msg_free(msg); + } + rc = 0; + +out: + return rc; +} + +int ospf_apiserver_handle_sync_lsdb(struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct listnode *node, *nnode; + uint32_t seqnum; + int rc = 0; + struct msg_sync_lsdb *smsg; + struct ospf_apiserver_param_t { + struct ospf_apiserver *apiserv; + struct lsa_filter_type *filter; + } param; + uint16_t mask; + struct route_node *rn; + struct ospf_lsa *lsa; + struct ospf *ospf; + struct ospf_area *area; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + /* Get request sequence number */ + seqnum = msg_get_seq(msg); + /* Set sync msg. */ + smsg = (struct msg_sync_lsdb *)STREAM_DATA(msg->s); + + /* Set parameter struct. */ + param.apiserv = apiserv; + param.filter = &smsg->filter; + + /* Remember mask. */ + mask = ntohs(smsg->filter.typemask); + + /* Iterate over all areas. */ + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + int i; + uint32_t *area_id = NULL; + + /* Compare area_id with area_ids in sync request. */ + if ((i = smsg->filter.num_areas) > 0) { + /* Let area_id point to the list of area IDs, + * which is at the end of smsg->filter. */ + area_id = (uint32_t *)(&smsg->filter + 1); + while (i) { + if (*area_id == area->area_id.s_addr) { + break; + } + i--; + area_id++; + } + } else { + i = 1; + } + + /* If area was found, then i>0 here. */ + if (i) { + /* Check msg type. */ + if (mask & Power2[OSPF_ROUTER_LSA]) + LSDB_LOOP (ROUTER_LSDB(area), rn, lsa) + apiserver_sync_callback( + lsa, (void *)¶m, seqnum); + if (mask & Power2[OSPF_NETWORK_LSA]) + LSDB_LOOP (NETWORK_LSDB(area), rn, lsa) + apiserver_sync_callback( + lsa, (void *)¶m, seqnum); + if (mask & Power2[OSPF_SUMMARY_LSA]) + LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa) + apiserver_sync_callback( + lsa, (void *)¶m, seqnum); + if (mask & Power2[OSPF_ASBR_SUMMARY_LSA]) + LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa) + apiserver_sync_callback( + lsa, (void *)¶m, seqnum); + if (mask & Power2[OSPF_OPAQUE_LINK_LSA]) + LSDB_LOOP (OPAQUE_LINK_LSDB(area), rn, lsa) + apiserver_sync_callback( + lsa, (void *)¶m, seqnum); + if (mask & Power2[OSPF_OPAQUE_AREA_LSA]) + LSDB_LOOP (OPAQUE_AREA_LSDB(area), rn, lsa) + apiserver_sync_callback( + lsa, (void *)¶m, seqnum); + } + } + + /* For AS-external LSAs */ + if (ospf->lsdb) { + if (mask & Power2[OSPF_AS_EXTERNAL_LSA]) + LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) + apiserver_sync_callback(lsa, (void *)¶m, + seqnum); + } + + /* For AS-external opaque LSAs */ + if (ospf->lsdb) { + if (mask & Power2[OSPF_OPAQUE_AS_LSA]) + LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa) + apiserver_sync_callback(lsa, (void *)¶m, + seqnum); + } + + /* Send a reply back to client with return code */ + rc = ospf_apiserver_send_reply(apiserv, seqnum, rc); + return rc; +} + +/* + * ----------------------------------------------------------- + * Followings are functions for synchronization. + * ----------------------------------------------------------- + */ + +int ospf_apiserver_handle_sync_reachable(struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + struct route_table *rt = ospf->all_rtrs; + uint32_t seqnum = msg_get_seq(msg); + struct in_addr *a, *abuf; + struct msg_reachable_change *areach; + struct msg *amsg; + uint mcount, count; + int _rc, rc = 0; + + if (!rt) + goto out; + + /* send all adds based on current reachable routers */ + a = abuf = XCALLOC(MTYPE_APISERVER, sizeof(struct in_addr) * rt->count); + for (struct route_node *rn = route_top(rt); rn; rn = route_next(rn)) + if (listhead((struct list *)rn->info)) + *a++ = rn->p.u.prefix4; + + assert((a - abuf) <= (long)rt->count); + count = (a - abuf); + + a = abuf; + while (count && !rc) { + amsg = new_msg_reachable_change(seqnum, count, a, 0, NULL); + areach = (struct msg_reachable_change *)STREAM_DATA(amsg->s); + mcount = ntohs(areach->nadd) + ntohs(areach->nremove); + assert(mcount <= count); + a = a + mcount; + count -= mcount; + rc = ospf_apiserver_send_msg(apiserv, amsg); + msg_free(amsg); + } + XFREE(MTYPE_APISERVER, abuf); + +out: + /* Send a reply back to client with return code */ + _rc = ospf_apiserver_send_reply(apiserv, seqnum, rc); + rc = rc ? rc : _rc; + apiserv->reachable_sync = !rc; + return rc; +} + +int ospf_apiserver_handle_sync_ism(struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + struct listnode *anode, *inode; + struct ospf_area *area; + struct ospf_interface *oi; + struct msg *m; + uint32_t seqnum = msg_get_seq(msg); + int _rc, rc = 0; + + /* walk all areas */ + for (ALL_LIST_ELEMENTS_RO(ospf->areas, anode, area)) { + /* walk all interfaces */ + for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) { + m = new_msg_ism_change(seqnum, oi->address->u.prefix4, + area->area_id, oi->state); + rc = ospf_apiserver_send_msg(apiserv, m); + msg_free(m); + if (rc) + break; + } + if (rc) + break; + } + /* Send a reply back to client with return code */ + _rc = ospf_apiserver_send_reply(apiserv, seqnum, rc); + return rc ? rc : _rc; +} + + +int ospf_apiserver_handle_sync_nsm(struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + struct listnode *anode, *inode; + struct ospf_area *area; + struct ospf_interface *oi; + struct ospf_neighbor *nbr; + struct route_node *rn; + struct msg *m; + uint32_t seqnum = msg_get_seq(msg); + int _rc, rc = 0; + + /* walk all areas */ + for (ALL_LIST_ELEMENTS_RO(ospf->areas, anode, area)) { + /* walk all interfaces */ + for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) { + /* walk all neighbors */ + for (rn = route_top(oi->nbrs); rn; + rn = route_next(rn)) { + nbr = rn->info; + if (!nbr) + continue; + m = new_msg_nsm_change( + seqnum, oi->address->u.prefix4, + nbr->src, nbr->router_id, nbr->state); + rc = ospf_apiserver_send_msg(apiserv, m); + msg_free(m); + if (rc) + break; + } + if (rc) + break; + } + if (rc) + break; + } + /* Send a reply back to client with return code */ + _rc = ospf_apiserver_send_reply(apiserv, seqnum, rc); + return rc ? rc : _rc; +} + + +int ospf_apiserver_handle_sync_router_id(struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + uint32_t seqnum = msg_get_seq(msg); + struct msg *m; + int _rc, rc = 0; + + m = new_msg_router_id_change(seqnum, ospf->router_id); + rc = ospf_apiserver_send_msg(apiserv, m); + msg_free(m); + + /* Send a reply back to client with return code */ + _rc = ospf_apiserver_send_reply(apiserv, seqnum, rc); + return rc ? rc : _rc; +} + +/* ----------------------------------------------------------- + * Following are functions to originate or update LSA + * from an application. + * ----------------------------------------------------------- + */ + +/* Create a new internal opaque LSA by taking prototype and filling in + missing fields such as age, sequence number, advertising router, + checksum and so on. The interface parameter is used for type 9 + LSAs, area parameter for type 10. Type 11 LSAs do neither need area + nor interface. */ + +struct ospf_lsa *ospf_apiserver_opaque_lsa_new(struct ospf_area *area, + struct ospf_interface *oi, + struct lsa_header *protolsa) +{ + struct stream *s; + struct lsa_header *newlsa; + struct ospf_lsa *new = NULL; + uint8_t options = 0x0; + uint16_t length; + + struct ospf *ospf; + + if (oi && oi->ospf) + ospf = oi->ospf; + else + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + assert(ospf); + + /* Create a stream for internal opaque LSA */ + if ((s = stream_new(OSPF_MAX_LSA_SIZE)) == NULL) { + zlog_warn("%s: stream_new failed", __func__); + return NULL; + } + + newlsa = (struct lsa_header *)STREAM_DATA(s); + + /* XXX If this is a link-local LSA or an AS-external LSA, how do we + have to set options? */ + + if (area) { + options = LSA_OPTIONS_GET(area); + options |= LSA_OPTIONS_NSSA_GET(area); + } + + options |= OSPF_OPTION_O; /* Don't forget to set option bit */ + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: Creating an Opaque-LSA instance", + protolsa->type, &protolsa->id); + } + + /* Set opaque-LSA header fields. */ + lsa_header_set(s, options, protolsa->type, protolsa->id, + ospf->router_id); + + /* Set opaque-LSA body fields. */ + stream_put(s, ((uint8_t *)protolsa) + sizeof(struct lsa_header), + ntohs(protolsa->length) - sizeof(struct lsa_header)); + + /* Determine length of LSA. */ + length = stream_get_endp(s); + newlsa->length = htons(length); + + /* Create OSPF LSA. */ + new = ospf_lsa_new_and_data(length); + + new->area = area; + new->oi = oi; + new->vrf_id = ospf->vrf_id; + + SET_FLAG(new->flags, OSPF_LSA_SELF); + memcpy(new->data, newlsa, length); + stream_free(s); + + return new; +} + + +int ospf_apiserver_is_ready_type9(struct ospf_interface *oi) +{ + /* We can always handle getting opaque's even if we can't flood them */ + return 1; +} + +int ospf_apiserver_is_ready_type10(struct ospf_area *area) +{ + /* We can always handle getting opaque's even if we can't flood them */ + return 1; +} + +int ospf_apiserver_is_ready_type11(struct ospf *ospf) +{ + /* We can always handle getting opaque's even if we can't flood them */ + return 1; +} + + +int ospf_apiserver_handle_originate_request(struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct msg_originate_request *omsg; + struct lsa_header *data; + struct ospf_lsa *new; + struct ospf_lsa *old; + struct ospf_area *area = NULL; + struct ospf_interface *oi = NULL; + struct ospf_lsdb *lsdb = NULL; + struct ospf *ospf; + int lsa_type, opaque_type; + int ready = 0; + int rc = 0; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + /* Extract opaque LSA data from message */ + omsg = (struct msg_originate_request *)STREAM_DATA(msg->s); + data = &omsg->data; + + /* Determine interface for type9 or area for type10 LSAs. */ + switch (data->type) { + case OSPF_OPAQUE_LINK_LSA: + oi = ospf_apiserver_if_lookup_by_addr(omsg->ifaddr); + if (!oi) { + zlog_warn("%s: unknown interface %pI4", __func__, + &omsg->ifaddr); + rc = OSPF_API_NOSUCHINTERFACE; + goto out; + } + area = oi->area; + lsdb = area->lsdb; + break; + case OSPF_OPAQUE_AREA_LSA: + area = ospf_area_lookup_by_area_id(ospf, omsg->area_id); + if (!area) { + zlog_warn("%s: unknown area %pI4", __func__, + &omsg->area_id); + rc = OSPF_API_NOSUCHAREA; + goto out; + } + lsdb = area->lsdb; + break; + case OSPF_OPAQUE_AS_LSA: + lsdb = ospf->lsdb; + break; + default: + /* We can only handle opaque types here */ + zlog_warn("%s: Cannot originate non-opaque LSA type %d", + __func__, data->type); + rc = OSPF_API_ILLEGALLSATYPE; + goto out; + } + + /* Check if we registered this opaque type */ + lsa_type = data->type; + opaque_type = GET_OPAQUE_TYPE(ntohl(data->id.s_addr)); + + if (!apiserver_is_opaque_type_registered(apiserv, lsa_type, + opaque_type)) { + zlog_warn("%s: LSA-type(%d)/Opaque-type(%d): Not registered", + __func__, lsa_type, opaque_type); + rc = OSPF_API_OPAQUETYPENOTREGISTERED; + goto out; + } + + /* Make sure that the neighbors are ready before we can originate */ + switch (data->type) { + case OSPF_OPAQUE_LINK_LSA: + ready = ospf_apiserver_is_ready_type9(oi); + break; + case OSPF_OPAQUE_AREA_LSA: + ready = ospf_apiserver_is_ready_type10(area); + break; + case OSPF_OPAQUE_AS_LSA: + ready = ospf_apiserver_is_ready_type11(ospf); + break; + default: + break; + } + + if (!ready) { + zlog_warn("Neighbors not ready to originate type %d", + data->type); + rc = OSPF_API_NOTREADY; + goto out; + } + + /* Create OSPF's internal opaque LSA representation */ + new = ospf_apiserver_opaque_lsa_new(area, oi, data); + if (!new) { + rc = OSPF_API_NOMEMORY; /* XXX */ + goto out; + } + + /* Determine if LSA is new or an update for an existing one. */ + old = ospf_lsdb_lookup(lsdb, new); + + if (!old || !ospf_opaque_is_owned(old)) { + /* New LSA install in LSDB. */ + rc = ospf_apiserver_originate1(new, old); + } else { + /* + * Keep the new LSA instance in the "waiting place" until the + * next + * refresh timing. If several LSA update requests for the same + * LSID + * have issued by peer, the last one takes effect. + */ + new->lsdb = &apiserv->reserve; + ospf_lsdb_add(&apiserv->reserve, new); + + /* Kick the scheduler function. */ + ospf_opaque_lsa_refresh_schedule(old); + } + +out: + + /* Send a reply back to client with return code */ + rc = ospf_apiserver_send_reply(apiserv, ntohl(msg->hdr.msgseq), rc); + return rc; +} + + +/* ----------------------------------------------------------- + * Flood an LSA within its flooding scope. + * ----------------------------------------------------------- + */ + +/* XXX We can probably use ospf_flood_through instead of this function + but then we need the neighbor parameter. If we set nbr to + NULL then ospf_flood_through crashes due to dereferencing NULL. */ + +void ospf_apiserver_flood_opaque_lsa(struct ospf_lsa *lsa) +{ + assert(lsa); + + switch (lsa->data->type) { + case OSPF_OPAQUE_LINK_LSA: + /* Increment counters? XXX */ + + /* Flood LSA through local network. */ + ospf_flood_through_area(lsa->area, NULL /*nbr */, lsa); + break; + case OSPF_OPAQUE_AREA_LSA: + /* Update LSA origination count. */ + assert(lsa->area); + lsa->area->ospf->lsa_originate_count++; + + /* Flood LSA through area. */ + ospf_flood_through_area(lsa->area, NULL /*nbr */, lsa); + break; + case OSPF_OPAQUE_AS_LSA: { + struct ospf *ospf; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + assert(ospf); + + /* Increment counters? XXX */ + + /* Flood LSA through AS. */ + ospf_flood_through_as(ospf, NULL /*nbr */, lsa); + break; + } + } +} + +int ospf_apiserver_originate1(struct ospf_lsa *lsa, struct ospf_lsa *old) +{ + struct ospf *ospf; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + assert(ospf); + + if (old) { + /* + * An old LSA exists that we didn't originate it in this + * session. Dump it, but increment past it's seqnum. + */ + assert(!ospf_opaque_is_owned(old)); + if (IS_DEBUG_OSPF_CLIENT_API) + zlog_debug( + "LSA[Type%d:%pI4]: OSPF API Server Originate LSA Old Seq: 0x%x Age: %d", + old->data->type, &old->data->id, + ntohl(old->data->ls_seqnum), + ntohl(old->data->ls_age)); + if (IS_LSA_MAX_SEQ(old)) { + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "%s: old LSA at maxseq", __func__); + return -1; + } + lsa->data->ls_seqnum = lsa_seqnum_increment(old); + ospf_discard_from_db(ospf, old->lsdb, old); + } + if (IS_DEBUG_OSPF_CLIENT_API) + zlog_debug( + "LSA[Type%d:%pI4]: OSPF API Server Originate LSA New Seq: 0x%x Age: %d", + lsa->data->type, &lsa->data->id, + ntohl(lsa->data->ls_seqnum), ntohl(lsa->data->ls_age)); + + /* Install this LSA into LSDB. */ + if (ospf_lsa_install(ospf, lsa->oi, lsa) == NULL) { + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "%s: ospf_lsa_install failed", __func__); + return -1; + } + +/* Flood LSA within scope */ + +#ifdef NOTYET + /* + * NB: Modified version of "ospf_flood_though ()" accepts NULL "inbr" + * parameter, and thus it does not cause SIGSEGV error. + */ + ospf_flood_through(NULL /*nbr */, lsa); +#else /* NOTYET */ + + ospf_apiserver_flood_opaque_lsa(lsa); +#endif /* NOTYET */ + + return 0; +} + + +/* Opaque LSAs of type 9 on a specific interface can now be + originated. Tell clients that registered type 9. */ +int ospf_apiserver_lsa9_originator(void *arg) +{ + struct ospf_interface *oi; + + oi = (struct ospf_interface *)arg; + if (listcount(apiserver_list) > 0) { + ospf_apiserver_clients_notify_ready_type9(oi); + } + return 0; +} + +int ospf_apiserver_lsa10_originator(void *arg) +{ + struct ospf_area *area; + + area = (struct ospf_area *)arg; + if (listcount(apiserver_list) > 0) { + ospf_apiserver_clients_notify_ready_type10(area); + } + return 0; +} + +int ospf_apiserver_lsa11_originator(void *arg) +{ + struct ospf *ospf; + + ospf = (struct ospf *)arg; + if (listcount(apiserver_list) > 0) { + ospf_apiserver_clients_notify_ready_type11(ospf); + } + return 0; +} + + +/* Periodically refresh opaque LSAs so that they do not expire in + other routers. */ +struct ospf_lsa *ospf_apiserver_lsa_refresher(struct ospf_lsa *lsa) +{ + struct ospf_apiserver *apiserv; + struct ospf_lsa *new = NULL; + struct ospf *ospf; + + assert(lsa); + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + assert(ospf); + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: OSPF API Server LSA Refresher", + lsa->data->type, &lsa->data->id); + } + + apiserv = lookup_apiserver_by_lsa(lsa); + if (!apiserv) { + zlog_warn("%s: LSA[%s]: No apiserver?", __func__, + dump_lsa_key(lsa)); + lsa->data->ls_age = + htons(OSPF_LSA_MAXAGE); /* Flush it anyway. */ + goto out; + } + + /* Check if updated version of LSA instance has already prepared. */ + new = ospf_lsdb_lookup(&apiserv->reserve, lsa); + if (!new) { + if (IS_LSA_MAXAGE(lsa)) { + ospf_opaque_lsa_flush_schedule(lsa); + goto out; + } + + /* This is a periodic refresh, driven by core OSPF mechanism. */ + new = ospf_apiserver_opaque_lsa_new(lsa->area, lsa->oi, + lsa->data); + if (!new) { + zlog_warn("%s: Cannot create a new LSA?", __func__); + goto out; + } + } else { + /* This is a forcible refresh, requested by OSPF-API client. */ + ospf_lsdb_delete(&apiserv->reserve, new); + new->lsdb = NULL; + } + + /* Increment sequence number */ + new->data->ls_seqnum = lsa_seqnum_increment(lsa); + + /* New LSA is in same area. */ + new->area = lsa->area; + SET_FLAG(new->flags, OSPF_LSA_SELF); + + /* Install LSA into LSDB. */ + if (ospf_lsa_install(ospf, new->oi, new) == NULL) { + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "%s: ospf_lsa_install failed", __func__); + ospf_lsa_unlock(&new); + goto out; + } + +/* Flood updated LSA through interface, area or AS */ + +#ifdef NOTYET + ospf_flood_through(NULL /*nbr */, new); +#endif /* NOTYET */ + ospf_apiserver_flood_opaque_lsa(new); + + /* Debug logging. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: Refresh Opaque LSA", + new->data->type, &new->data->id); + ospf_lsa_header_dump(new->data); + } + +out: + return new; +} + + +/* ----------------------------------------------------------- + * Following are functions to delete LSAs + * ----------------------------------------------------------- + */ + +int ospf_apiserver_handle_delete_request(struct ospf_apiserver *apiserv, + struct msg *msg) +{ + struct msg_delete_request *dmsg; + struct ospf_lsa *old; + struct ospf_area *area = NULL; + struct ospf_interface *oi = NULL; + struct in_addr id; + int lsa_type, opaque_type; + int rc = 0; + struct ospf *ospf; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + assert(ospf); + + /* Extract opaque LSA from message */ + dmsg = (struct msg_delete_request *)STREAM_DATA(msg->s); + + /* Lookup area for link-local and area-local opaque LSAs */ + switch (dmsg->lsa_type) { + case OSPF_OPAQUE_LINK_LSA: + oi = ospf_apiserver_if_lookup_by_addr(dmsg->addr); + if (!oi) { + zlog_warn("%s: unknown interface %pI4", __func__, + &dmsg->addr); + rc = OSPF_API_NOSUCHINTERFACE; + goto out; + } + area = oi->area; + break; + case OSPF_OPAQUE_AREA_LSA: + area = ospf_area_lookup_by_area_id(ospf, dmsg->addr); + if (!area) { + zlog_warn("%s: unknown area %pI4", __func__, + &dmsg->addr); + rc = OSPF_API_NOSUCHAREA; + goto out; + } + break; + case OSPF_OPAQUE_AS_LSA: + /* AS-external opaque LSAs have no designated area */ + area = NULL; + break; + default: + zlog_warn("%s: Cannot delete non-opaque LSA type %d", __func__, + dmsg->lsa_type); + rc = OSPF_API_ILLEGALLSATYPE; + goto out; + } + + /* Check if we registered this opaque type */ + lsa_type = dmsg->lsa_type; + opaque_type = dmsg->opaque_type; + + if (!apiserver_is_opaque_type_registered(apiserv, lsa_type, + opaque_type)) { + zlog_warn("%s: LSA-type(%d)/Opaque-type(%d): Not registered", + __func__, lsa_type, opaque_type); + rc = OSPF_API_OPAQUETYPENOTREGISTERED; + goto out; + } + + /* opaque_id is in network byte order */ + id.s_addr = htonl( + SET_OPAQUE_LSID(dmsg->opaque_type, ntohl(dmsg->opaque_id))); + + /* + * Even if the target LSA has once scheduled to flush, it remains in + * the LSDB until it is finally handled by the maxage remover thread. + * Therefore, the lookup function below may return non-NULL result. + */ + old = ospf_lsa_lookup(ospf, area, dmsg->lsa_type, id, ospf->router_id); + if (!old) { + zlog_warn("%s: LSA[Type%d:%pI4] not in LSDB", __func__, + dmsg->lsa_type, &id); + rc = OSPF_API_NOSUCHLSA; + goto out; + } + + if (IS_DEL_ZERO_LEN_LSA(dmsg)) { + /* minimize the size of the withdrawal: */ + old->opaque_zero_len_delete = 1; + } + + /* Schedule flushing of LSA from LSDB */ + /* NB: Multiple scheduling will produce a warning message, but harmless. + */ + ospf_opaque_lsa_flush_schedule(old); + +out: + + /* Send reply back to client including return code */ + rc = ospf_apiserver_send_reply(apiserv, ntohl(msg->hdr.msgseq), rc); + return rc; +} + +/* Flush self-originated opaque LSA */ +static int apiserver_flush_opaque_type_callback(struct ospf_lsa *lsa, + void *p_arg, int int_arg) +{ + struct param_t { + struct ospf_apiserver *apiserv; + uint8_t lsa_type; + uint8_t opaque_type; + } * param; + + /* Sanity check */ + assert(lsa->data); + assert(p_arg); + param = (struct param_t *)p_arg; + + /* If LSA matches type and opaque type then delete it */ + if (IS_LSA_SELF(lsa) && lsa->data->type == param->lsa_type + && GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)) + == param->opaque_type) { + ospf_opaque_lsa_flush_schedule(lsa); + } + return 0; +} + +/* Delete self-originated opaque LSAs of a given opaque type. This + function is called when an application unregisters a given opaque + type or a connection to an application closes and all those opaque + LSAs need to be flushed the LSDB. */ +void ospf_apiserver_flush_opaque_lsa(struct ospf_apiserver *apiserv, + uint8_t lsa_type, uint8_t opaque_type) +{ + struct param_t { + struct ospf_apiserver *apiserv; + uint8_t lsa_type; + uint8_t opaque_type; + } param; + struct listnode *node, *nnode; + struct ospf *ospf; + struct ospf_area *area; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + assert(ospf); + + /* Set parameter struct. */ + param.apiserv = apiserv; + param.lsa_type = lsa_type; + param.opaque_type = opaque_type; + + switch (lsa_type) { + struct route_node *rn; + struct ospf_lsa *lsa; + + case OSPF_OPAQUE_LINK_LSA: + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) + LSDB_LOOP (OPAQUE_LINK_LSDB(area), rn, lsa) + apiserver_flush_opaque_type_callback( + lsa, (void *)¶m, 0); + break; + case OSPF_OPAQUE_AREA_LSA: + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) + LSDB_LOOP (OPAQUE_AREA_LSDB(area), rn, lsa) + apiserver_flush_opaque_type_callback( + lsa, (void *)¶m, 0); + break; + case OSPF_OPAQUE_AS_LSA: + LSDB_LOOP (OPAQUE_LINK_LSDB(ospf), rn, lsa) + apiserver_flush_opaque_type_callback(lsa, + (void *)¶m, 0); + break; + default: + break; + } + return; +} + + +/* ----------------------------------------------------------- + * Following are callback functions to handle opaque types + * ----------------------------------------------------------- + */ + +int ospf_apiserver_new_if(struct interface *ifp) +{ + struct ospf_interface *oi; + + /* For some strange reason it seems possible that we are invoked + with an interface that has no name. This seems to happen during + initialization. Return if this happens */ + + if (ifp->name[0] == '\0') { + /* interface has empty name */ + zlog_warn("%s: interface has no name?", __func__); + return 0; + } + + /* zlog_warn for debugging */ + zlog_warn("ospf_apiserver_new_if"); + zlog_warn("ifp name=%s status=%d index=%d", ifp->name, ifp->status, + ifp->ifindex); + + if (ifp->name[0] == '\0') { + /* interface has empty name */ + zlog_warn("%s: interface has no name?", __func__); + return 0; + } + + oi = ospf_apiserver_if_lookup_by_ifp(ifp); + + if (!oi) { + /* This interface is known to Zebra but not to OSPF daemon yet. + */ + zlog_warn("%s: interface %s not known to OSPFd?", __func__, + ifp->name); + return 0; + } + + assert(oi); + + /* New interface added to OSPF, tell clients about it */ + if (listcount(apiserver_list) > 0) { + ospf_apiserver_clients_notify_new_if(oi); + } + return 0; +} + +int ospf_apiserver_del_if(struct interface *ifp) +{ + struct ospf_interface *oi; + + /* zlog_warn for debugging */ + zlog_warn("%s ifp name=%s status=%d index=%d", __func__, ifp->name, + ifp->status, ifp->ifindex); + + oi = ospf_apiserver_if_lookup_by_ifp(ifp); + + if (!oi) { + /* This interface is known to Zebra but not to OSPF daemon + anymore. No need to tell clients about it */ + zlog_warn("ifp name=%s not known to OSPFd", ifp->name); + return 0; + } + + /* Interface deleted, tell clients about it */ + if (listcount(apiserver_list) > 0) { + ospf_apiserver_clients_notify_del_if(oi); + } + return 0; +} + +void ospf_apiserver_ism_change(struct ospf_interface *oi, int old_state) +{ + /* Tell clients about interface change */ + + /* zlog_warn for debugging */ + zlog_warn("%s", __func__); + if (listcount(apiserver_list) > 0) { + ospf_apiserver_clients_notify_ism_change(oi); + } + + zlog_warn("%s oi->ifp->name=%s old_state=%d oi->state=%d", __func__, + oi->ifp->name, old_state, oi->state); +} + +void ospf_apiserver_nsm_change(struct ospf_neighbor *nbr, int old_status) +{ + /* Neighbor status changed, tell clients about it */ + zlog_warn("%s", __func__); + if (listcount(apiserver_list) > 0) { + ospf_apiserver_clients_notify_nsm_change(nbr); + } +} + +void ospf_apiserver_show_info(struct vty *vty, struct json_object *json, + struct ospf_lsa *lsa) +{ + struct opaque_lsa { + struct lsa_header header; + uint8_t data[1]; /* opaque data have variable length. This is + start + address */ + }; + struct opaque_lsa *olsa; + int opaquelen; + + olsa = (struct opaque_lsa *)lsa->data; + + if (VALID_OPAQUE_INFO_LEN(lsa->data)) + opaquelen = ntohs(lsa->data->length) - OSPF_LSA_HEADER_SIZE; + else + opaquelen = 0; + + /* Output information about opaque LSAs */ + if (json) + json_object_string_addf(json, "opaqueData", "%*pHXn", + (int)opaquelen, olsa->data); + else if (vty != NULL) { + int i; + vty_out(vty, + " Added using OSPF API: %u octets of opaque data %s\n", + opaquelen, + VALID_OPAQUE_INFO_LEN(lsa->data) ? "" + : "(Invalid length?)"); + vty_out(vty, " Opaque data: "); + + for (i = 0; i < opaquelen; i++) { + vty_out(vty, "0x%x ", olsa->data[i]); + } + vty_out(vty, "\n"); + } else { + int i; + zlog_debug( + " Added using OSPF API: %u octets of opaque data %s", + opaquelen, + VALID_OPAQUE_INFO_LEN(lsa->data) ? "" + : "(Invalid length?)"); + zlog_debug(" Opaque data: "); + + for (i = 0; i < opaquelen; i++) { + zlog_debug("0x%x ", olsa->data[i]); + } + } + return; +} + +/* ----------------------------------------------------------- + * Following are functions to notify clients about events + * ----------------------------------------------------------- + */ + +/* Send a message to all clients. This is useful for messages + that need to be notified to all clients (such as interface + changes) */ + +void ospf_apiserver_clients_notify_all(struct msg *msg) +{ + struct listnode *node, *nnode; + struct ospf_apiserver *apiserv; + + /* Send message to all clients */ + for (ALL_LIST_ELEMENTS(apiserver_list, node, nnode, apiserv)) + ospf_apiserver_send_msg(apiserv, msg); +} + +/* An interface is now ready to accept opaque LSAs. Notify all + clients that registered to use this opaque type */ +void ospf_apiserver_clients_notify_ready_type9(struct ospf_interface *oi) +{ + struct listnode *node, *nnode; + struct msg *msg; + struct ospf_apiserver *apiserv; + + assert(oi); + if (!oi->address) { + zlog_warn("Interface has no address?"); + return; + } + + if (!ospf_apiserver_is_ready_type9(oi)) { + zlog_warn("Interface not ready for type 9?"); + return; + } + + for (ALL_LIST_ELEMENTS(apiserver_list, node, nnode, apiserv)) { + struct listnode *node2, *nnode2; + struct registered_opaque_type *r; + + for (ALL_LIST_ELEMENTS(apiserv->opaque_types, node2, nnode2, + r)) { + if (r->lsa_type == OSPF_OPAQUE_LINK_LSA) { + msg = new_msg_ready_notify( + 0, OSPF_OPAQUE_LINK_LSA, r->opaque_type, + oi->address->u.prefix4); + if (!msg) { + zlog_warn( + "%s: new_msg_ready_notify failed", + __func__); +#ifdef NOTYET + /* Cannot allocate new message. What + * should we do? */ + ospf_apiserver_free(apiserv); +#endif + goto out; + } + + ospf_apiserver_send_msg(apiserv, msg); + msg_free(msg); + } + } + } + +out: + return; +} + +void ospf_apiserver_clients_notify_ready_type10(struct ospf_area *area) +{ + struct listnode *node, *nnode; + struct msg *msg; + struct ospf_apiserver *apiserv; + + assert(area); + + if (!ospf_apiserver_is_ready_type10(area)) { + zlog_warn("Area not ready for type 10?"); + return; + } + + for (ALL_LIST_ELEMENTS(apiserver_list, node, nnode, apiserv)) { + struct listnode *node2, *nnode2; + struct registered_opaque_type *r; + + for (ALL_LIST_ELEMENTS(apiserv->opaque_types, node2, nnode2, + r)) { + if (r->lsa_type == OSPF_OPAQUE_AREA_LSA) { + msg = new_msg_ready_notify( + 0, OSPF_OPAQUE_AREA_LSA, r->opaque_type, + area->area_id); + if (!msg) { + zlog_warn( + "%s: new_msg_ready_nofity failed", + __func__); +#ifdef NOTYET + /* Cannot allocate new message. What + * should we do? */ + ospf_apiserver_free(apiserv); +#endif + goto out; + } + + ospf_apiserver_send_msg(apiserv, msg); + msg_free(msg); + } + } + } + +out: + return; +} + + +void ospf_apiserver_clients_notify_ready_type11(struct ospf *top) +{ + struct listnode *node, *nnode; + struct msg *msg; + struct in_addr id_null = {.s_addr = 0L}; + struct ospf_apiserver *apiserv; + + assert(top); + + if (!ospf_apiserver_is_ready_type11(top)) { + zlog_warn("AS not ready for type 11?"); + return; + } + + for (ALL_LIST_ELEMENTS(apiserver_list, node, nnode, apiserv)) { + struct listnode *node2, *nnode2; + struct registered_opaque_type *r; + + for (ALL_LIST_ELEMENTS(apiserv->opaque_types, node2, nnode2, + r)) { + if (r->lsa_type == OSPF_OPAQUE_AS_LSA) { + msg = new_msg_ready_notify( + 0, OSPF_OPAQUE_AS_LSA, r->opaque_type, + id_null); + if (!msg) { + zlog_warn( + "%s: new_msg_ready_notify failed", + __func__); +#ifdef NOTYET + /* Cannot allocate new message. What + * should we do? */ + ospf_apiserver_free(apiserv); +#endif + goto out; + } + + ospf_apiserver_send_msg(apiserv, msg); + msg_free(msg); + } + } + } + +out: + return; +} + +void ospf_apiserver_clients_notify_new_if(struct ospf_interface *oi) +{ + struct msg *msg; + + msg = new_msg_new_if(0, oi->address->u.prefix4, oi->area->area_id); + if (msg != NULL) { + ospf_apiserver_clients_notify_all(msg); + msg_free(msg); + } +} + +void ospf_apiserver_clients_notify_del_if(struct ospf_interface *oi) +{ + struct msg *msg; + + msg = new_msg_del_if(0, oi->address->u.prefix4); + if (msg != NULL) { + ospf_apiserver_clients_notify_all(msg); + msg_free(msg); + } +} + +void ospf_apiserver_clients_notify_ism_change(struct ospf_interface *oi) +{ + struct msg *msg; + struct in_addr ifaddr = {.s_addr = 0L}; + struct in_addr area_id = {.s_addr = 0L}; + + assert(oi); + assert(oi->ifp); + + if (oi->address) { + ifaddr = oi->address->u.prefix4; + } + if (oi->area) { + area_id = oi->area->area_id; + } + + msg = new_msg_ism_change(0, ifaddr, area_id, oi->state); + if (!msg) { + zlog_warn("%s: msg_new failed", __func__); + return; + } + + ospf_apiserver_clients_notify_all(msg); + msg_free(msg); +} + +void ospf_apiserver_clients_notify_nsm_change(struct ospf_neighbor *nbr) +{ + struct msg *msg; + struct in_addr ifaddr; + struct in_addr nbraddr; + + assert(nbr); + + ifaddr = nbr->oi->address->u.prefix4; + + nbraddr = nbr->address.u.prefix4; + + msg = new_msg_nsm_change(0, ifaddr, nbraddr, nbr->router_id, + nbr->state); + if (!msg) { + zlog_warn("%s: msg_new failed", __func__); + return; + } + + ospf_apiserver_clients_notify_all(msg); + msg_free(msg); +} + +static int apiserver_clients_lsa_change_notify(uint8_t msgtype, + struct ospf_lsa *lsa) +{ + struct msg *msg; + struct listnode *node, *nnode; + struct ospf_apiserver *apiserv; + + /* Default area for AS-External and Opaque11 LSAs */ + struct in_addr area_id = {.s_addr = 0L}; + + /* Default interface for non Opaque9 LSAs */ + struct in_addr ifaddr = {.s_addr = 0L}; + + if (lsa->area) { + area_id = lsa->area->area_id; + } + if (lsa->data->type == OSPF_OPAQUE_LINK_LSA) { + assert(lsa->oi); + ifaddr = lsa->oi->address->u.prefix4; + } + + /* Prepare message that can be sent to clients that have a matching + filter */ + msg = new_msg_lsa_change_notify(msgtype, 0L, /* no sequence number */ + ifaddr, area_id, + lsa->flags & OSPF_LSA_SELF, lsa->data); + if (!msg) { + zlog_warn("%s: msg_new failed", __func__); + return -1; + } + + /* Now send message to all clients with a matching filter */ + for (ALL_LIST_ELEMENTS(apiserver_list, node, nnode, apiserv)) { + struct lsa_filter_type *filter; + uint16_t mask; + uint32_t *area; + int i; + + /* Check filter for this client. */ + filter = apiserv->filter; + + /* Check area IDs in case of non AS-E LSAs. + * If filter has areas (num_areas > 0), + * then one of the areas must match the area ID of this LSA. */ + + i = filter->num_areas; + if ((lsa->data->type == OSPF_AS_EXTERNAL_LSA) + || (lsa->data->type == OSPF_OPAQUE_AS_LSA)) { + i = 0; + } + + if (i > 0) { + area = (uint32_t *)(filter + 1); + while (i) { + if (*area == area_id.s_addr) { + break; + } + i--; + area++; + } + } else { + i = 1; + } + + if (i > 0) { + /* Area match. Check LSA type. */ + mask = ntohs(filter->typemask); + + if (mask & Power2[lsa->data->type]) { + /* Type also matches. Check origin. */ + if ((filter->origin == ANY_ORIGIN) + || (filter->origin == IS_LSA_SELF(lsa))) { + ospf_apiserver_send_msg(apiserv, msg); + } + } + } + } + /* Free message since it is not used anymore */ + msg_free(msg); + + return 0; +} + + +/* ------------------------------------------------------------- + * Following are hooks invoked when LSAs are updated or deleted + * ------------------------------------------------------------- + */ + + +int ospf_apiserver_lsa_update(struct ospf_lsa *lsa) +{ + + /* Only notify this update if the LSA's age is smaller than + MAXAGE. Otherwise clients would see LSA updates with max age just + before they are deleted from the LSDB. LSA delete messages have + MAXAGE too but should not be filtered. */ + if (IS_LSA_MAXAGE(lsa)) + return 0; + return apiserver_clients_lsa_change_notify(MSG_LSA_UPDATE_NOTIFY, lsa); +} + +int ospf_apiserver_lsa_delete(struct ospf_lsa *lsa) +{ + return apiserver_clients_lsa_change_notify(MSG_LSA_DELETE_NOTIFY, lsa); +} + +/* ------------------------------------------------------------- + * Reachable functions + * ------------------------------------------------------------- + */ + +static inline int cmp_route_nodes(struct route_node *orn, + struct route_node *nrn) +{ + if (!orn) + return 1; + else if (!nrn) + return -1; + + uint32_t opn = ntohl(orn->p.u.prefix4.s_addr); + uint32_t npn = ntohl(nrn->p.u.prefix4.s_addr); + if (opn < npn) + return -1; + else if (opn > npn) + return 1; + else + return 0; +} + +void ospf_apiserver_notify_reachable(struct route_table *ort, + struct route_table *nrt) +{ + struct msg *msg; + struct msg_reachable_change *areach; + struct route_node *orn, *nrn; + const uint insz = sizeof(struct in_addr); + struct in_addr *abuf = NULL, *dbuf = NULL; + struct in_addr *a = NULL, *d = NULL; + uint nadd, nremove; + int cmp; + + if (!ort && !nrt) { + if (IS_DEBUG_OSPF_CLIENT_API) + zlog_debug("%s: no routing tables", __func__); + return; + } + if (nrt && nrt->count) + a = abuf = XCALLOC(MTYPE_APISERVER, insz * nrt->count); + if (ort && ort->count) + d = dbuf = XCALLOC(MTYPE_APISERVER, insz * ort->count); + + /* walk both tables */ + orn = ort ? route_top(ort) : NULL; + nrn = nrt ? route_top(nrt) : NULL; + while (orn || nrn) { + if (orn && !listhead((struct list *)orn->info)) { + orn = route_next(orn); + continue; + } + if (nrn && !listhead((struct list *)nrn->info)) { + nrn = route_next(nrn); + continue; + } + cmp = cmp_route_nodes(orn, nrn); + if (!cmp) { + /* if old == new advance old and new */ + if (IS_DEBUG_OSPF_CLIENT_API) + zlog_debug("keeping router id: %pI4", + &orn->p.u.prefix4); + orn = route_next(orn); + nrn = route_next(nrn); + } else if (cmp < 0) { + assert(d != NULL); /* Silence SA warning */ + + /* if old < new, delete old, advance old */ + *d++ = orn->p.u.prefix4; + if (IS_DEBUG_OSPF_CLIENT_API) + zlog_debug("removing router id: %pI4", + &orn->p.u.prefix4); + orn = route_next(orn); + } else { + assert(a != NULL); /* Silence SA warning */ + + /* if new < old, add new, advance new */ + *a++ = nrn->p.u.prefix4; + if (IS_DEBUG_OSPF_CLIENT_API) + zlog_debug("adding router id: %pI4", + &nrn->p.u.prefix4); + nrn = route_next(nrn); + } + } + + nadd = abuf ? (a - abuf) : 0; + nremove = dbuf ? (d - dbuf) : 0; + a = abuf; + d = dbuf; + + while (nadd + nremove) { + msg = new_msg_reachable_change(0, nadd, a, nremove, d); + areach = (struct msg_reachable_change *)STREAM_DATA(msg->s); + + a += ntohs(areach->nadd); + nadd = nadd - ntohs(areach->nadd); + + d += ntohs(areach->nremove); + nremove = nremove - ntohs(areach->nremove); + + if (IS_DEBUG_OSPF_CLIENT_API) + zlog_debug("%s: adding %d removing %d", __func__, + ntohs(areach->nadd), ntohs(areach->nremove)); + ospf_apiserver_clients_notify_all(msg); + msg_free(msg); + } + if (abuf) + XFREE(MTYPE_APISERVER, abuf); + if (dbuf) + XFREE(MTYPE_APISERVER, dbuf); +} + + +void ospf_apiserver_clients_notify_router_id_change(struct in_addr router_id) +{ + struct msg *msg; + + msg = new_msg_router_id_change(0, router_id); + if (!msg) { + zlog_warn("%s: new_msg_router_id_change failed", __func__); + return; + } + + ospf_apiserver_clients_notify_all(msg); + msg_free(msg); +} + + +#endif /* SUPPORT_OSPF_API */ diff --git a/ospfd/ospf_apiserver.h b/ospfd/ospf_apiserver.h new file mode 100644 index 0000000..0aaf67c --- /dev/null +++ b/ospfd/ospf_apiserver.h @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Server side of OSPF API. + * Copyright (C) 2001, 2002 Ralph Keller + */ + +#ifndef _OSPF_APISERVER_H +#define _OSPF_APISERVER_H + +#include +#include "ospf_api.h" +#include "ospf_lsdb.h" + +/* List of opaque types that application registered */ +struct registered_opaque_type { + uint8_t lsa_type; + uint8_t opaque_type; +}; + + +/* Server instance for each accepted client connection. */ +struct ospf_apiserver { + /* Socket connections for synchronous commands and asynchronous + notifications */ + int fd_sync; /* synchronous requests */ + struct sockaddr_in peer_sync; + + int fd_async; /* asynchronous notifications */ + struct sockaddr_in peer_async; + + /* List of all opaque types that application registers to use. Using + a single connection with the OSPF daemon, multiple + pairs can be registered. However, each + combination can only be registered once by all applications. */ + struct list *opaque_types; /* of type registered_opaque_type */ + + /* Temporary storage for LSA instances to be refreshed. */ + struct ospf_lsdb reserve; + + /* Sync reachable routers */ + bool reachable_sync; + + /* filter for LSA update/delete notifies */ + struct lsa_filter_type *filter; + + /* Fifo buffers for outgoing messages */ + struct msg_fifo *out_sync_fifo; + struct msg_fifo *out_async_fifo; + + /* Read and write threads */ + struct event *t_sync_read; +#ifdef USE_ASYNC_READ + struct event *t_async_read; +#endif /* USE_ASYNC_READ */ + struct event *t_sync_write; + struct event *t_async_write; +}; + +enum ospf_apiserver_event { + OSPF_APISERVER_ACCEPT, + OSPF_APISERVER_SYNC_READ, +#ifdef USE_ASYNC_READ + OSPF_APISERVER_ASYNC_READ, +#endif /* USE_ASYNC_READ */ + OSPF_APISERVER_SYNC_WRITE, + OSPF_APISERVER_ASYNC_WRITE +}; + +/* ----------------------------------------------------------- + * Following are functions to manage client connections. + * ----------------------------------------------------------- + */ + +extern unsigned short ospf_apiserver_getport(void); +extern int ospf_apiserver_init(void); +extern void ospf_apiserver_term(void); +extern struct ospf_apiserver *ospf_apiserver_new(int fd_sync, int fd_async); +extern void ospf_apiserver_free(struct ospf_apiserver *apiserv); +extern void ospf_apiserver_event(enum ospf_apiserver_event event, int fd, + struct ospf_apiserver *apiserv); +extern int ospf_apiserver_serv_sock_family(unsigned short port, int family); +extern void ospf_apiserver_accept(struct event *thread); +extern void ospf_apiserver_read(struct event *thread); +extern void ospf_apiserver_sync_write(struct event *thread); +extern void ospf_apiserver_async_write(struct event *thread); +extern int ospf_apiserver_send_reply(struct ospf_apiserver *apiserv, + uint32_t seqnr, uint8_t rc); + +/* ----------------------------------------------------------- + * Following are message handler functions + * ----------------------------------------------------------- + */ + +extern int ospf_apiserver_lsa9_originator(void *arg); +extern int ospf_apiserver_lsa10_originator(void *arg); +extern int ospf_apiserver_lsa11_originator(void *arg); + +extern void ospf_apiserver_clients_notify_all(struct msg *msg); + +extern void +ospf_apiserver_clients_notify_ready_type9(struct ospf_interface *oi); +extern void ospf_apiserver_clients_notify_ready_type10(struct ospf_area *area); +extern void ospf_apiserver_clients_notify_ready_type11(struct ospf *top); + +extern void ospf_apiserver_clients_notify_new_if(struct ospf_interface *oi); +extern void ospf_apiserver_clients_notify_del_if(struct ospf_interface *oi); +extern void ospf_apiserver_clients_notify_ism_change(struct ospf_interface *oi); +extern void ospf_apiserver_clients_notify_nsm_change(struct ospf_neighbor *nbr); +extern void +ospf_apiserver_clients_notify_router_id_change(struct in_addr router_id); + +extern int ospf_apiserver_is_ready_type9(struct ospf_interface *oi); +extern int ospf_apiserver_is_ready_type10(struct ospf_area *area); +extern int ospf_apiserver_is_ready_type11(struct ospf *ospf); + +extern void ospf_apiserver_notify_ready_type9(struct ospf_apiserver *apiserv); +extern void ospf_apiserver_notify_ready_type10(struct ospf_apiserver *apiserv); +extern void ospf_apiserver_notify_ready_type11(struct ospf_apiserver *apiserv); + +extern int ospf_apiserver_handle_msg(struct ospf_apiserver *apiserv, + struct msg *msg); +extern int +ospf_apiserver_handle_register_opaque_type(struct ospf_apiserver *apiserv, + struct msg *msg); +extern int +ospf_apiserver_handle_unregister_opaque_type(struct ospf_apiserver *apiserv, + struct msg *msg); +extern int ospf_apiserver_handle_register_event(struct ospf_apiserver *apiserv, + struct msg *msg); +extern int +ospf_apiserver_handle_originate_request(struct ospf_apiserver *apiserv, + struct msg *msg); +extern int ospf_apiserver_handle_delete_request(struct ospf_apiserver *apiserv, + struct msg *msg); +extern int ospf_apiserver_handle_sync_lsdb(struct ospf_apiserver *apiserv, + struct msg *msg); +extern int ospf_apiserver_handle_sync_reachable(struct ospf_apiserver *apiserv, + struct msg *msg); +extern int ospf_apiserver_handle_sync_ism(struct ospf_apiserver *apiserv, + struct msg *msg); +extern int ospf_apiserver_handle_sync_nsm(struct ospf_apiserver *apiserv, + struct msg *msg); +extern int ospf_apiserver_handle_sync_router_id(struct ospf_apiserver *apiserv, + struct msg *msg); + +extern void ospf_apiserver_notify_reachable(struct route_table *ort, + struct route_table *nrt); + +/* ----------------------------------------------------------- + * Following are functions for LSA origination/deletion + * ----------------------------------------------------------- + */ + +extern int ospf_apiserver_register_opaque_type(struct ospf_apiserver *apiserver, + uint8_t lsa_type, + uint8_t opaque_type); +extern int +ospf_apiserver_unregister_opaque_type(struct ospf_apiserver *apiserver, + uint8_t lsa_type, uint8_t opaque_type); +extern struct ospf_lsa * +ospf_apiserver_opaque_lsa_new(struct ospf_area *area, struct ospf_interface *oi, + struct lsa_header *protolsa); +extern struct ospf_interface * +ospf_apiserver_if_lookup_by_addr(struct in_addr address); +extern struct ospf_interface * +ospf_apiserver_if_lookup_by_ifp(struct interface *ifp); +extern int ospf_apiserver_originate1(struct ospf_lsa *lsa, + struct ospf_lsa *old); +extern void ospf_apiserver_flood_opaque_lsa(struct ospf_lsa *lsa); + + +/* ----------------------------------------------------------- + * Following are callback functions to handle opaque types + * ----------------------------------------------------------- + */ + +extern int ospf_apiserver_new_if(struct interface *ifp); +extern int ospf_apiserver_del_if(struct interface *ifp); +extern void ospf_apiserver_ism_change(struct ospf_interface *oi, + int old_status); +extern void ospf_apiserver_nsm_change(struct ospf_neighbor *nbr, + int old_status); +extern void ospf_apiserver_config_write_router(struct vty *vty); +extern void ospf_apiserver_config_write_if(struct vty *vty, + struct interface *ifp); +extern void ospf_apiserver_show_info(struct vty *vty, struct json_object *json, + struct ospf_lsa *lsa); +extern int ospf_ospf_apiserver_lsa_originator(void *arg); +extern struct ospf_lsa *ospf_apiserver_lsa_refresher(struct ospf_lsa *lsa); +extern void ospf_apiserver_flush_opaque_lsa(struct ospf_apiserver *apiserv, + uint8_t lsa_type, + uint8_t opaque_type); + +/* ----------------------------------------------------------- + * Following are hooks when LSAs are updated or deleted + * ----------------------------------------------------------- + */ + + +/* Hooks that are invoked from ospf opaque module */ + +extern int ospf_apiserver_lsa_update(struct ospf_lsa *lsa); +extern int ospf_apiserver_lsa_delete(struct ospf_lsa *lsa); + +#endif /* _OSPF_APISERVER_H */ diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c new file mode 100644 index 0000000..5baad17 --- /dev/null +++ b/ospfd/ospf_asbr.c @@ -0,0 +1,1224 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF AS Boundary Router functions. + * Copyright (C) 1999, 2000 Kunihiro Ishiguro, Toshiaki Takada + */ + +#include + +#include "frrevent.h" +#include "memory.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "vty.h" +#include "filter.h" +#include "log.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_errors.h" + +/* Remove external route. */ +void ospf_external_route_remove(struct ospf *ospf, struct prefix_ipv4 *p) +{ + struct route_node *rn; + struct ospf_route * or ; + + rn = route_node_lookup(ospf->old_external_route, (struct prefix *)p); + if (rn) + if ((or = rn->info)) { + zlog_info("Route[%pFX]: external path deleted", p); + + /* Remove route from zebra. */ + if (or->type == OSPF_DESTINATION_NETWORK) + ospf_zebra_delete( + ospf, (struct prefix_ipv4 *)&rn->p, or); + + ospf_route_free(or); + rn->info = NULL; + + route_unlock_node(rn); + route_unlock_node(rn); + return; + } + + zlog_info("Route[%pFX]: no such external path", p); +} + +/* Add an External info for AS-external-LSA. */ +struct external_info *ospf_external_info_new(struct ospf *ospf, uint8_t type, + unsigned short instance) +{ + struct external_info *new; + + new = XCALLOC(MTYPE_OSPF_EXTERNAL_INFO, sizeof(struct external_info)); + new->ospf = ospf; + new->type = type; + new->instance = instance; + new->to_be_processed = 0; + + ospf_reset_route_map_set_values(&new->route_map_set); + return new; +} + +static void ospf_external_info_free(struct external_info *ei) +{ + XFREE(MTYPE_OSPF_EXTERNAL_INFO, ei); +} + +void ospf_reset_route_map_set_values(struct route_map_set_values *values) +{ + values->metric = -1; + values->metric_type = -1; +} + +int ospf_route_map_set_compare(struct route_map_set_values *values1, + struct route_map_set_values *values2) +{ + return values1->metric == values2->metric + && values1->metric_type == values2->metric_type; +} + +/* Add an External info for AS-external-LSA. */ +struct external_info * +ospf_external_info_add(struct ospf *ospf, uint8_t type, unsigned short instance, + struct prefix_ipv4 p, ifindex_t ifindex, + struct in_addr nexthop, route_tag_t tag, uint32_t metric) +{ + struct external_info *new; + struct route_node *rn; + struct ospf_external *ext; + + ext = ospf_external_lookup(ospf, type, instance); + if (!ext) + ext = ospf_external_add(ospf, type, instance); + + rn = route_node_get(EXTERNAL_INFO(ext), (struct prefix *)&p); + /* If old info exists, -- discard new one or overwrite with new one? */ + if (rn && rn->info) { + new = rn->info; + if ((new->ifindex == ifindex) + && (new->nexthop.s_addr == nexthop.s_addr) + && (new->tag == tag)) { + route_unlock_node(rn); + return NULL; /* NULL => no LSA to refresh */ + } + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "Redistribute[%s][%d][%u]: %pFX discarding old info with NH %pI4.", + ospf_redist_string(type), instance, + ospf->vrf_id, &p, &nexthop.s_addr); + XFREE(MTYPE_OSPF_EXTERNAL_INFO, rn->info); + } + + /* Create new External info instance. */ + new = ospf_external_info_new(ospf, type, instance); + new->p = p; + new->ifindex = ifindex; + new->nexthop = nexthop; + new->tag = tag; + new->orig_tag = tag; + new->aggr_route = NULL; + new->metric = metric; + new->min_metric = 0; + new->max_metric = OSPF_LS_INFINITY; + + /* we don't unlock rn from the get() because we're attaching the info */ + if (rn) + rn->info = new; + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug( + "Redistribute[%s][%u]: %pFX external info created, with NH %pI4, metric:%u", + ospf_redist_string(type), ospf->vrf_id, &p, + &nexthop.s_addr, metric); + } + return new; +} + +void ospf_external_info_delete(struct ospf *ospf, uint8_t type, + unsigned short instance, struct prefix_ipv4 p) +{ + struct route_node *rn; + struct ospf_external *ext; + + ext = ospf_external_lookup(ospf, type, instance); + if (!ext) + return; + + rn = route_node_lookup(EXTERNAL_INFO(ext), (struct prefix *)&p); + if (rn) { + ospf_external_info_free(rn->info); + rn->info = NULL; + route_unlock_node(rn); + route_unlock_node(rn); + } +} + +struct external_info *ospf_external_info_lookup(struct ospf *ospf, uint8_t type, + unsigned short instance, + struct prefix_ipv4 *p) +{ + struct route_node *rn; + struct ospf_external *ext; + + ext = ospf_external_lookup(ospf, type, instance); + if (!ext) + return NULL; + + rn = route_node_lookup(EXTERNAL_INFO(ext), (struct prefix *)p); + if (rn) { + route_unlock_node(rn); + if (rn->info) + return rn->info; + } + + return NULL; +} + +struct ospf_lsa *ospf_external_info_find_lsa(struct ospf *ospf, + struct prefix_ipv4 *p) +{ + struct ospf_lsa *lsa; + struct as_external_lsa *al; + struct in_addr mask, id; + + /* First search the lsdb with address specific LSID + * where all the host bits are set, if there a matched + * LSA, return. + * Ex: For route 10.0.0.0/16, LSID is 10.0.255.255 + * If no lsa with above LSID, use received address as + * LSID and check if any LSA in LSDB. + * If LSA found, check if the mask is same b/w the matched + * LSA and received prefix, if same then it is the LSA for + * this prefix. + * Ex: For route 10.0.0.0/16, LSID is 10.0.0.0 + */ + + masklen2ip(p->prefixlen, &mask); + id.s_addr = p->prefix.s_addr | (~mask.s_addr); + lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA, id, + ospf->router_id); + if (lsa) { + if (p->prefixlen == IPV4_MAX_BITLEN) { + al = (struct as_external_lsa *)lsa->data; + + if (mask.s_addr != al->mask.s_addr) + return NULL; + } + return lsa; + } + + lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA, + p->prefix, ospf->router_id); + + if (lsa) { + al = (struct as_external_lsa *)lsa->data; + if (mask.s_addr == al->mask.s_addr) + return lsa; + } + + return NULL; +} + + +/* Update ASBR status. */ +void ospf_asbr_status_update(struct ospf *ospf, uint8_t status) +{ + zlog_info("ASBR[%s:Status:%d]: Update", + ospf_get_name(ospf), status); + + /* ASBR on. */ + if (status) { + /* Already ASBR. */ + if (IS_OSPF_ASBR(ospf)) { + zlog_info("ASBR[%s:Status:%d]: Already ASBR", + ospf_get_name(ospf), status); + return; + } + SET_FLAG(ospf->flags, OSPF_FLAG_ASBR); + } else { + /* Already non ASBR. */ + if (!IS_OSPF_ASBR(ospf)) { + zlog_info("ASBR[%s:Status:%d]: Already non ASBR", + ospf_get_name(ospf), status); + return; + } + UNSET_FLAG(ospf->flags, OSPF_FLAG_ASBR); + } + + /* Transition from/to status ASBR, schedule timer. */ + ospf_spf_calculate_schedule(ospf, SPF_FLAG_ASBR_STATUS_CHANGE); + ospf_router_lsa_update(ospf); +} + +/* If there's redistribution configured, we need to refresh external + * LSAs (e.g. when default-metric changes or NSSA settings change). + */ +static void ospf_asbr_redist_update_timer(struct event *thread) +{ + struct ospf *ospf = EVENT_ARG(thread); + int type; + + ospf->t_asbr_redist_update = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Running ASBR redistribution update on timer"); + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + struct list *red_list; + struct listnode *node; + struct ospf_redist *red; + + red_list = ospf->redist[type]; + if (!red_list) + continue; + + for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) + ospf_external_lsa_refresh_type(ospf, type, + red->instance, + LSA_REFRESH_FORCE); + } + + ospf_external_lsa_refresh_default(ospf); +} + +void ospf_schedule_asbr_redist_update(struct ospf *ospf) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Scheduling ASBR redistribution update"); + + event_add_timer(master, ospf_asbr_redist_update_timer, ospf, + OSPF_ASBR_REDIST_UPDATE_DELAY, + &ospf->t_asbr_redist_update); +} + +void ospf_redistribute_withdraw(struct ospf *ospf, uint8_t type, + unsigned short instance) +{ + struct route_node *rn; + struct external_info *ei; + struct ospf_external *ext; + + ext = ospf_external_lookup(ospf, type, instance); + if (!ext) + return; + + /* Delete external info for specified type. */ + if (!EXTERNAL_INFO(ext)) + return; + + for (rn = route_top(EXTERNAL_INFO(ext)); rn; rn = route_next(rn)) { + ei = rn->info; + + if (!ei) + continue; + + struct ospf_external_aggr_rt *aggr; + + if (is_default_prefix4(&ei->p) + && ospf->default_originate != DEFAULT_ORIGINATE_NONE) + continue; + + aggr = ei->aggr_route; + + if (aggr) + ospf_unlink_ei_from_aggr(ospf, aggr, ei); + else if (ospf_external_info_find_lsa(ospf, &ei->p)) + ospf_external_lsa_flush(ospf, type, &ei->p, + ei->ifindex /*, ei->nexthop */); + + ospf_external_info_free(ei); + route_unlock_node(rn); + rn->info = NULL; + } +} + + +/* External Route Aggregator Handlers */ +bool is_valid_summary_addr(struct prefix_ipv4 *p) +{ + /* Default prefix validation*/ + if (p->prefix.s_addr == INADDR_ANY) + return false; + + /*Host route shouldn't be configured as summary addres*/ + if (p->prefixlen == IPV4_MAX_BITLEN) + return false; + + return true; +} +void ospf_asbr_external_aggregator_init(struct ospf *instance) +{ + instance->rt_aggr_tbl = route_table_init(); + + instance->t_external_aggr = NULL; + + instance->aggr_action = 0; + + instance->aggr_delay_interval = OSPF_EXTL_AGGR_DEFAULT_DELAY; +} + +static unsigned int ospf_external_rt_hash_key(const void *data) +{ + const struct external_info *ei = data; + unsigned int key = 0; + + key = prefix_hash_key(&ei->p); + return key; +} + +static bool ospf_external_rt_hash_cmp(const void *d1, const void *d2) +{ + const struct external_info *ei1 = d1; + const struct external_info *ei2 = d2; + + return prefix_same((struct prefix *)&ei1->p, (struct prefix *)&ei2->p); +} + +static struct ospf_external_aggr_rt * +ospf_external_aggregator_new(struct prefix_ipv4 *p) +{ + struct ospf_external_aggr_rt *aggr; + + aggr = (struct ospf_external_aggr_rt *)XCALLOC( + MTYPE_OSPF_EXTERNAL_RT_AGGR, + sizeof(struct ospf_external_aggr_rt)); + + if (!aggr) + return NULL; + + aggr->p.family = p->family; + aggr->p.prefix = p->prefix; + aggr->p.prefixlen = p->prefixlen; + aggr->match_extnl_hash = hash_create(ospf_external_rt_hash_key, + ospf_external_rt_hash_cmp, + "Ospf external route hash"); + return aggr; +} + +static void ospf_aggr_handle_external_info(void *data) +{ + struct external_info *ei = (struct external_info *)data; + struct ospf_external_aggr_rt *aggr = NULL; + struct ospf *ospf = ei->ospf; + struct ospf_lsa *lsa = NULL; + + ei->aggr_route = NULL; + + ei->to_be_processed = true; + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug("%s: Handle extrenal route(%pI4/%d)", __func__, + &ei->p.prefix, ei->p.prefixlen); + + assert(ospf); + + if (!ospf_redistribute_check(ospf, ei, NULL)) + return; + + aggr = ospf_external_aggr_match(ospf, &ei->p); + if (aggr) { + (void)ospf_originate_summary_lsa(ospf, aggr, ei); + return; + } + + lsa = ospf_external_info_find_lsa(ospf, &ei->p); + if (lsa) + ospf_external_lsa_refresh(ospf, lsa, ei, LSA_REFRESH_FORCE, 1); + else + (void)ospf_external_lsa_originate(ospf, ei); +} + +static void ospf_aggr_unlink_external_info(void *data) +{ + struct external_info *ei = (struct external_info *)data; + + ei->aggr_route = NULL; + + ei->to_be_processed = true; +} + +void ospf_external_aggregator_free(struct ospf_external_aggr_rt *aggr) +{ + hash_clean_and_free(&aggr->match_extnl_hash, + (void *)ospf_aggr_unlink_external_info); + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug("%s: Release the aggregator Address(%pI4/%d)", + __func__, &aggr->p.prefix, aggr->p.prefixlen); + + XFREE(MTYPE_OSPF_EXTERNAL_RT_AGGR, aggr); +} + +static void ospf_external_aggr_add(struct ospf *ospf, + struct ospf_external_aggr_rt *aggr) +{ + struct route_node *rn; + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug("%s: Adding Aggregate route to Aggr table (%pI4/%d)", + __func__, &aggr->p.prefix, aggr->p.prefixlen); + rn = route_node_get(ospf->rt_aggr_tbl, (struct prefix *)&aggr->p); + if (rn->info) + route_unlock_node(rn); + else + rn->info = aggr; +} + +static void ospf_external_aggr_delete(struct ospf *ospf, struct route_node *rn) +{ + struct ospf_external_aggr_rt *aggr = rn->info; + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug("%s: Deleting Aggregate route (%pI4/%d)", __func__, + &aggr->p.prefix, aggr->p.prefixlen); + + /* Sent a Max age LSA if it is already originated. */ + if (CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED)) { + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug("%s: Flushing Aggregate route (%pI4/%d)", + __func__, &aggr->p.prefix, + aggr->p.prefixlen); + ospf_external_lsa_flush(ospf, 0, &aggr->p, 0); + } + + rn->info = NULL; + route_unlock_node(rn); +} + +struct ospf_external_aggr_rt * +ospf_extrenal_aggregator_lookup(struct ospf *ospf, struct prefix_ipv4 *p) +{ + struct route_node *rn; + struct ospf_external_aggr_rt *summary_rt = NULL; + + rn = route_node_lookup(ospf->rt_aggr_tbl, (struct prefix *)p); + if (rn) { + summary_rt = rn->info; + route_unlock_node(rn); + return summary_rt; + } + return NULL; +} + +struct ospf_external_aggr_rt *ospf_external_aggr_match(struct ospf *ospf, + struct prefix_ipv4 *p) +{ + struct route_node *node; + struct ospf_external_aggr_rt *summary_rt = NULL; + + node = route_node_match(ospf->rt_aggr_tbl, (struct prefix *)p); + if (node) { + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + if (node->info) { + struct ospf_external_aggr_rt *ag = node->info; + + zlog_debug( + "%s: Matching aggregator found.prefix:%pI4/%d Aggregator %pI4/%d", + __func__, &p->prefix, p->prefixlen, + &ag->p.prefix, ag->p.prefixlen); + } + + summary_rt = node->info; + route_unlock_node(node); + return summary_rt; + } + return NULL; +} + +void ospf_unlink_ei_from_aggr(struct ospf *ospf, + struct ospf_external_aggr_rt *aggr, + struct external_info *ei) +{ + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "%s: Unlinking extrenal route(%pI4/%d) from aggregator(%pI4/%d), external route count:%ld", + __func__, &ei->p.prefix, ei->p.prefixlen, + &aggr->p.prefix, aggr->p.prefixlen, + OSPF_EXTERNAL_RT_COUNT(aggr)); + hash_release(aggr->match_extnl_hash, ei); + ei->aggr_route = NULL; + + /* Flush the aggreagte route if matching + * external route count becomes zero. + */ + if (!OSPF_EXTERNAL_RT_COUNT(aggr) + && CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED)) { + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug("%s: Flushing the aggreagte route (%pI4/%d)", + __func__, &aggr->p.prefix, + aggr->p.prefixlen); + + /* Flush the aggregate LSA */ + ospf_external_lsa_flush(ospf, 0, &aggr->p, 0); + + /* Unset the Origination flag */ + UNSET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED); + } +} + +static void ospf_link_ei_to_aggr(struct ospf_external_aggr_rt *aggr, + struct external_info *ei) +{ + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "%s: Linking extrenal route(%pI4/%d) to aggregator(%pI4/%d)", + __func__, &ei->p.prefix, ei->p.prefixlen, + &aggr->p.prefix, aggr->p.prefixlen); + (void)hash_get(aggr->match_extnl_hash, ei, hash_alloc_intern); + ei->aggr_route = aggr; +} + +struct ospf_lsa *ospf_originate_summary_lsa(struct ospf *ospf, + struct ospf_external_aggr_rt *aggr, + struct external_info *ei) +{ + struct ospf_lsa *lsa; + struct external_info ei_aggr; + struct as_external_lsa *asel; + struct ospf_external_aggr_rt *old_aggr; + route_tag_t tag = 0; + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug("%s: Prepare to originate Summary route(%pI4/%d)", + __func__, &aggr->p.prefix, aggr->p.prefixlen); + + /* This case to handle when the overlapping aggregator address + * is availbe.Best match will be considered.So need to delink + * from old aggregator and link to the new aggr. + */ + if (ei->aggr_route) { + if (ei->aggr_route != aggr) { + old_aggr = ei->aggr_route; + ospf_unlink_ei_from_aggr(ospf, old_aggr, ei); + } + } + + /* Add the external route to hash table */ + ospf_link_ei_to_aggr(aggr, ei); + + lsa = ospf_external_info_find_lsa(ospf, &aggr->p); + /* Don't originate external LSA, + * If it is configured not to advertise. + */ + if (CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_NO_ADVERTISE)) { + /* If it is already originated as external LSA, + * But, it is configured not to advertise then + * flush the originated external lsa. + */ + if (lsa) + ospf_external_lsa_flush(ospf, 0, &aggr->p, 0); + UNSET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED); + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "%s: Don't originate the summary address,It is configured to not-advertise.", + __func__); + return NULL; + } + + /* Prepare the extrenal_info for aggregator */ + memset(&ei_aggr, 0, sizeof(ei_aggr)); + ei_aggr.p = aggr->p; + ei_aggr.tag = aggr->tag; + ei_aggr.type = 0; + ei_aggr.instance = ospf->instance; + ei_aggr.route_map_set.metric = -1; + ei_aggr.route_map_set.metric_type = -1; + + /* Summary route already originated, + * So, Do nothing. + */ + if (CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED)) { + if (!lsa) { + flog_warn(EC_OSPF_LSA_MISSING, + "%s: Could not refresh/originate %pI4/%d", + __func__, &aggr->p.prefix, aggr->p.prefixlen); + return NULL; + } + + asel = (struct as_external_lsa *)lsa->data; + tag = (unsigned long)ntohl(asel->e[0].route_tag); + + /* If tag modified , then re-originate the route + * with modified tag details. + */ + if (tag != ei_aggr.tag) { + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "%s: Route tag changed(old:%d new:%d,So refresh the summary route.(%pI4/%d)", + __func__, tag, ei_aggr.tag, + &aggr->p.prefix, aggr->p.prefixlen); + + ospf_external_lsa_refresh(ospf, lsa, &ei_aggr, + LSA_REFRESH_FORCE, 1); + } + return lsa; + } + + if (lsa && IS_LSA_MAXAGE(lsa)) { + /* This is special case. + * If a summary route need to be originated but where + * summary route already exist in lsdb with maxage, then + * it need to be refreshed. + */ + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "%s: LSA is in MAX-AGE so refreshing LSA(%pI4/%d)", + __func__, &aggr->p.prefix, aggr->p.prefixlen); + + ospf_external_lsa_refresh(ospf, lsa, &ei_aggr, + LSA_REFRESH_FORCE, 1); + SET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED); + return lsa; + } + + /* If the external route prefix same as aggregate route + * and if external route is already originated as TYPE-5 + * then it need to be refreshed and originate bit should + * be set. + */ + if (lsa && prefix_same((struct prefix *)&ei_aggr.p, + (struct prefix *)&ei->p)) { + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "%s: External route prefix is same as aggr so refreshing LSA(%pI4/%d)", + __func__, &aggr->p.prefix, aggr->p.prefixlen); + ospf_external_lsa_refresh(ospf, lsa, &ei_aggr, + LSA_REFRESH_FORCE, 1); + SET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED); + return lsa; + } + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug("%s: Originate Summary route(%pI4/%d)", __func__, + &aggr->p.prefix, aggr->p.prefixlen); + + /* Originate summary LSA */ + lsa = ospf_external_lsa_originate(ospf, &ei_aggr); + if (lsa) { + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug("%s: Set the origination bit for aggregator", + __func__); + SET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED); + } + + return lsa; +} +void ospf_unset_all_aggr_flag(struct ospf *ospf) +{ + struct route_node *rn = NULL; + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug("Unset the origination bit for all aggregator"); + + for (rn = route_top(ospf->rt_aggr_tbl); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + struct ospf_external_aggr_rt *aggr = rn->info; + + UNSET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED); + } +} + +static void ospf_delete_all_marked_aggregators(struct ospf *ospf) +{ + struct route_node *rn = NULL; + + /* Loop through all the aggregators, Delete all aggregators + * which are marked as DELETE. Set action to NONE for remaining + * aggregators + */ + for (rn = route_top(ospf->rt_aggr_tbl); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + struct ospf_external_aggr_rt *aggr = rn->info; + + if (aggr->action != OSPF_ROUTE_AGGR_DEL) { + aggr->action = OSPF_ROUTE_AGGR_NONE; + continue; + } + ospf_external_aggr_delete(ospf, rn); + ospf_external_aggregator_free(aggr); + } +} + +static void ospf_handle_aggregated_exnl_rt(struct ospf *ospf, + struct ospf_external_aggr_rt *aggr, + struct external_info *ei) +{ + struct ospf_lsa *lsa; + struct as_external_lsa *al; + struct in_addr mask; + + /* Handling the case where the external route prefix + * and aggregate prefix is same + * If same don't flush the originated external LSA. + */ + if (prefix_same((struct prefix *)&aggr->p, (struct prefix *)&ei->p)) { + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "%s: External Route prefix same as Aggregator(%pI4/%d), so don't flush.", + __func__, &ei->p.prefix, ei->p.prefixlen); + return; + } + + lsa = ospf_external_info_find_lsa(ospf, &ei->p); + if (lsa) { + al = (struct as_external_lsa *)lsa->data; + masklen2ip(ei->p.prefixlen, &mask); + + if (mask.s_addr != al->mask.s_addr) + return; + + ospf_external_lsa_flush(ospf, ei->type, &ei->p, 0); + } +} + +static void ospf_handle_exnl_rt_after_aggr_del(struct ospf *ospf, + struct external_info *ei) +{ + struct ospf_lsa *lsa; + + /* Process only marked external routes. + * These routes were part of a deleted + * aggregator.So, originate now. + */ + if (!ei->to_be_processed) + return; + + ei->to_be_processed = false; + + lsa = ospf_external_info_find_lsa(ospf, &ei->p); + + if (lsa) + ospf_external_lsa_refresh(ospf, lsa, ei, LSA_REFRESH_FORCE, 0); + else { + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug("%s: Originate external route(%pI4/%d)", + __func__, &ei->p.prefix, ei->p.prefixlen); + + ospf_external_lsa_originate(ospf, ei); + } +} + +static void ospf_handle_external_aggr_add(struct ospf *ospf) +{ + struct external_info *ei; + struct route_node *rn = NULL; + struct route_table *rt = NULL; + int type = 0; + + /* Delete all the aggregators which are marked as + * OSPF_ROUTE_AGGR_DEL. + */ + ospf_delete_all_marked_aggregators(ospf); + + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { + struct list *ext_list; + struct listnode *node; + struct ospf_external *ext; + struct ospf_external_aggr_rt *aggr; + + ext_list = ospf->external[type]; + if (!ext_list) + continue; + + for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) { + rt = ext->external_info; + if (!rt) + continue; + + for (rn = route_top(rt); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + ei = rn->info; + if (is_default_prefix4(&ei->p)) + continue; + + /* Check the AS-external-LSA + * should be originated. + */ + if (!ospf_redistribute_check(ospf, ei, NULL)) + continue; + + aggr = ospf_external_aggr_match(ospf, &ei->p); + + /* If matching aggregator found, Add + * the external route reference to the + * aggregator and originate the aggr + * route if it is advertisable. + * flush the external LSA if it is + * already originated for this external + * prefix. + */ + if (aggr) { + ospf_originate_summary_lsa(ospf, aggr, + ei); + + /* All aggregated external rts + * are handled here. + */ + ospf_handle_aggregated_exnl_rt( + ospf, aggr, ei); + continue; + } + + /* External routes which are only out + * of aggregation will be handled here. + */ + ospf_handle_exnl_rt_after_aggr_del(ospf, ei); + } + } + } +} + +static void +ospf_aggr_handle_advertise_change(struct ospf *ospf, + struct ospf_external_aggr_rt *aggr, + struct external_info *ei_aggr) +{ + struct ospf_lsa *lsa; + + /* Check if advertise option modified. */ + if (CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_NO_ADVERTISE)) { + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "%s: Don't originate the summary address,It is configured to not-advertise.", + __func__); + + if (CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED)) { + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "%s: No-advertise,So Flush the Aggregate route(%pI4/%d)", + __func__, &aggr->p.prefix, + aggr->p.prefixlen); + + ospf_external_lsa_flush(ospf, 0, &aggr->p, 0); + + UNSET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED); + } + return; + } + + if (!CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED)) { + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug("%s: Now it is advatisable", __func__); + + lsa = ospf_external_info_find_lsa(ospf, &ei_aggr->p); + if (lsa && IS_LSA_MAXAGE(lsa)) { + /* This is special case. + * If a summary route need to be originated but where + * summary route already exist in lsdb with maxage, then + * it need to be refreshed. + */ + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "%s: It is already with Maxage, So refresh it (%pI4/%d)", + __func__, &aggr->p.prefix, + aggr->p.prefixlen); + + ospf_external_lsa_refresh(ospf, lsa, ei_aggr, + LSA_REFRESH_FORCE, 1); + + SET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_ORIGINATED); + + } else { + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "%s: Originate Aggregate LSA (%pI4/%d)", + __func__, &aggr->p.prefix, + aggr->p.prefixlen); + + /* Originate summary LSA */ + lsa = ospf_external_lsa_originate(ospf, ei_aggr); + if (lsa) + SET_FLAG(aggr->flags, + OSPF_EXTERNAL_AGGRT_ORIGINATED); + } + } +} + +static void ospf_handle_external_aggr_update(struct ospf *ospf) +{ + struct route_node *rn = NULL; + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug("%s: Process modified aggregators.", __func__); + + for (rn = route_top(ospf->rt_aggr_tbl); rn; rn = route_next(rn)) { + struct ospf_external_aggr_rt *aggr; + struct ospf_lsa *lsa = NULL; + struct as_external_lsa *asel = NULL; + struct external_info ei_aggr; + route_tag_t tag = 0; + + if (!rn->info) + continue; + + aggr = rn->info; + + if (aggr->action == OSPF_ROUTE_AGGR_DEL) { + aggr->action = OSPF_ROUTE_AGGR_NONE; + ospf_external_aggr_delete(ospf, rn); + + hash_clean_and_free( + &aggr->match_extnl_hash, + (void *)ospf_aggr_handle_external_info); + + ospf_external_aggregator_free(aggr); + } else if (aggr->action == OSPF_ROUTE_AGGR_MODIFY) { + + aggr->action = OSPF_ROUTE_AGGR_NONE; + + /* Prepare the extrenal_info for aggregator */ + memset(&ei_aggr, 0, sizeof(ei_aggr)); + ei_aggr.p = aggr->p; + ei_aggr.tag = aggr->tag; + ei_aggr.type = 0; + ei_aggr.instance = ospf->instance; + ei_aggr.route_map_set.metric = -1; + ei_aggr.route_map_set.metric_type = -1; + + /* Check if tag modified */ + if (CHECK_FLAG(aggr->flags, + OSPF_EXTERNAL_AGGRT_ORIGINATED)) { + lsa = ospf_external_info_find_lsa(ospf, + &ei_aggr.p); + if (!lsa) { + flog_warn(EC_OSPF_LSA_MISSING, + "%s: Could not refresh/originate %pI4/%d", + __func__, &aggr->p.prefix, + aggr->p.prefixlen); + continue; + } + + asel = (struct as_external_lsa *)lsa->data; + tag = (unsigned long)ntohl( + asel->e[0].route_tag); + + /* If tag modified , then re-originate the + * route with modified tag details. + */ + if (tag != ei_aggr.tag) { + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "%s: Route tag changed(old:%d new:%d,So refresh the summary route.(%pI4/%d)", + __func__, tag, + ei_aggr.tag, + &aggr->p.prefix, + aggr->p.prefixlen); + + ospf_external_lsa_refresh( + ospf, lsa, &ei_aggr, + LSA_REFRESH_FORCE, 1); + } + } + + /* Advertise option modified ? + * If so, handled it here. + */ + ospf_aggr_handle_advertise_change(ospf, aggr, &ei_aggr); + } + } +} + +static void ospf_asbr_external_aggr_process(struct event *thread) +{ + struct ospf *ospf = EVENT_ARG(thread); + int operation = 0; + + ospf->t_external_aggr = NULL; + operation = ospf->aggr_action; + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug("%s: operation:%d", __func__, operation); + + switch (operation) { + case OSPF_ROUTE_AGGR_ADD: + ospf_handle_external_aggr_add(ospf); + break; + case OSPF_ROUTE_AGGR_DEL: + case OSPF_ROUTE_AGGR_MODIFY: + ospf_handle_external_aggr_update(ospf); + break; + default: + break; + } +} +static void ospf_external_aggr_timer(struct ospf *ospf, + struct ospf_external_aggr_rt *aggr, + enum ospf_aggr_action_t operation) +{ + aggr->action = operation; + + if (ospf->t_external_aggr) { + if (ospf->aggr_action == OSPF_ROUTE_AGGR_ADD) { + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "%s: Not required to retsart timer,set is already added.", + __func__); + return; + } + + if (operation == OSPF_ROUTE_AGGR_ADD) { + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "%s, Restarting Aggregator delay timer.", + __func__); + EVENT_OFF(ospf->t_external_aggr); + } + } + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug("%s: Start Aggregator delay timer %u(in seconds).", + __func__, ospf->aggr_delay_interval); + + ospf->aggr_action = operation; + event_add_timer(master, ospf_asbr_external_aggr_process, ospf, + ospf->aggr_delay_interval, &ospf->t_external_aggr); +} + +int ospf_asbr_external_aggregator_set(struct ospf *ospf, struct prefix_ipv4 *p, + route_tag_t tag) +{ + struct ospf_external_aggr_rt *aggregator; + + aggregator = ospf_extrenal_aggregator_lookup(ospf, p); + + if (aggregator) { + if (CHECK_FLAG(aggregator->flags, + OSPF_EXTERNAL_AGGRT_NO_ADVERTISE)) + UNSET_FLAG(aggregator->flags, + OSPF_EXTERNAL_AGGRT_NO_ADVERTISE); + else if (aggregator->tag == tag) + return OSPF_SUCCESS; + + aggregator->tag = tag; + + ospf_external_aggr_timer(ospf, aggregator, + OSPF_ROUTE_AGGR_MODIFY); + } else { + aggregator = ospf_external_aggregator_new(p); + if (!aggregator) + return OSPF_FAILURE; + + aggregator->tag = tag; + + ospf_external_aggr_add(ospf, aggregator); + ospf_external_aggr_timer(ospf, aggregator, OSPF_ROUTE_AGGR_ADD); + } + + return OSPF_SUCCESS; +} + +int ospf_asbr_external_aggregator_unset(struct ospf *ospf, + struct prefix_ipv4 *p, route_tag_t tag) +{ + struct route_node *rn; + struct ospf_external_aggr_rt *aggr; + + rn = route_node_lookup(ospf->rt_aggr_tbl, (struct prefix *)p); + if (!rn) + return OSPF_INVALID; + route_unlock_node(rn); + + aggr = rn->info; + + if (tag && (tag != aggr->tag)) + return OSPF_INVALID; + + if (!OSPF_EXTERNAL_RT_COUNT(aggr)) { + ospf_external_aggr_delete(ospf, rn); + ospf_external_aggregator_free(aggr); + return OSPF_SUCCESS; + } + + ospf_external_aggr_timer(ospf, aggr, OSPF_ROUTE_AGGR_DEL); + + return OSPF_SUCCESS; +} + +int ospf_asbr_external_rt_no_advertise(struct ospf *ospf, struct prefix_ipv4 *p) +{ + struct ospf_external_aggr_rt *aggr; + route_tag_t tag = 0; + + aggr = ospf_extrenal_aggregator_lookup(ospf, p); + if (aggr) { + if (CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_NO_ADVERTISE)) + return OSPF_SUCCESS; + + SET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_NO_ADVERTISE); + + aggr->tag = tag; + + if (!OSPF_EXTERNAL_RT_COUNT(aggr)) + return OSPF_SUCCESS; + + ospf_external_aggr_timer(ospf, aggr, OSPF_ROUTE_AGGR_MODIFY); + } else { + aggr = ospf_external_aggregator_new(p); + + if (!aggr) + return OSPF_FAILURE; + + SET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_NO_ADVERTISE); + ospf_external_aggr_add(ospf, aggr); + ospf_external_aggr_timer(ospf, aggr, OSPF_ROUTE_AGGR_ADD); + } + + return OSPF_SUCCESS; +} + +int ospf_asbr_external_rt_advertise(struct ospf *ospf, struct prefix_ipv4 *p) +{ + struct route_node *rn; + struct ospf_external_aggr_rt *aggr; + + rn = route_node_lookup(ospf->rt_aggr_tbl, (struct prefix *)p); + if (!rn) + return OSPF_INVALID; + route_unlock_node(rn); + + aggr = rn->info; + + if (!CHECK_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_NO_ADVERTISE)) + return OSPF_INVALID; + + UNSET_FLAG(aggr->flags, OSPF_EXTERNAL_AGGRT_NO_ADVERTISE); + + if (!OSPF_EXTERNAL_RT_COUNT(aggr)) + return OSPF_SUCCESS; + + ospf_external_aggr_timer(ospf, aggr, OSPF_ROUTE_AGGR_MODIFY); + return OSPF_SUCCESS; +} + +int ospf_external_aggregator_timer_set(struct ospf *ospf, uint16_t interval) +{ + ospf->aggr_delay_interval = interval; + return OSPF_SUCCESS; +} diff --git a/ospfd/ospf_asbr.h b/ospfd/ospf_asbr.h new file mode 100644 index 0000000..6158d65 --- /dev/null +++ b/ospfd/ospf_asbr.h @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF AS Boundary Router functions. + * Copyright (C) 1999, 2000 Kunihiro Ishiguro, Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPF_ASBR_H +#define _ZEBRA_OSPF_ASBR_H + +struct route_map_set_values { + int32_t metric; + int32_t metric_type; +}; + +/* Redistributed external information. */ +struct external_info { + struct ospf *ospf; + + /* Type of source protocol. */ + uint8_t type; + + unsigned short instance; + + /* Prefix. */ + struct prefix_ipv4 p; + + /* Interface index. */ + ifindex_t ifindex; + + /* Nexthop address. */ + struct in_addr nexthop; + + /* Additional Route tag. */ + route_tag_t tag; + + /* Actual tag received from zebra*/ + route_tag_t orig_tag; + + uint32_t metric; + uint32_t min_metric; + uint32_t max_metric; + + struct route_map_set_values route_map_set; +#define ROUTEMAP_METRIC(E) (E)->route_map_set.metric +#define ROUTEMAP_METRIC_TYPE(E) (E)->route_map_set.metric_type + + /* Back pointer to summary address */ + struct ospf_external_aggr_rt *aggr_route; + + /* To identify the routes to be originated + * after a summary address deletion. + */ + bool to_be_processed; +}; + +#define OSPF_EXTL_AGGR_DEFAULT_DELAY 5 + +#define OSPF_EXTERNAL_RT_COUNT(aggr) \ + (((struct ospf_external_aggr_rt *)aggr)->match_extnl_hash->count) + +enum ospf_aggr_action_t { + OSPF_ROUTE_AGGR_NONE = 0, + OSPF_ROUTE_AGGR_ADD, + OSPF_ROUTE_AGGR_DEL, + OSPF_ROUTE_AGGR_MODIFY +}; + +#define OSPF_SUCCESS 1 +#define OSPF_FAILURE 0 +#define OSPF_INVALID -1 + +#define OSPF_EXTERNAL_AGGRT_NO_ADVERTISE 0x1 +#define OSPF_EXTERNAL_AGGRT_ORIGINATED 0x2 + +/* Data structures for external route aggregator */ +struct ospf_external_aggr_rt { + /* Prefix. */ + struct prefix_ipv4 p; + + /* Bit 1 : Dont advertise. + * Bit 2 : Originated as Type-5 + */ + uint8_t flags; + + /* Tag for summary route */ + route_tag_t tag; + + /* Action to be done at the delay + * timer expairy. + */ + enum ospf_aggr_action_t action; + + /* Hash Table of external routes */ + struct hash *match_extnl_hash; +}; + +#define OSPF_ASBR_CHECK_DELAY 30 +#define OSPF_ASBR_REDIST_UPDATE_DELAY 9 + +extern void ospf_external_route_remove(struct ospf *, struct prefix_ipv4 *); +extern struct external_info *ospf_external_info_new(struct ospf *, uint8_t, + unsigned short); +extern void ospf_reset_route_map_set_values(struct route_map_set_values *); +extern int ospf_route_map_set_compare(struct route_map_set_values *, + struct route_map_set_values *); +extern struct external_info * +ospf_external_info_add(struct ospf *, uint8_t, unsigned short, + struct prefix_ipv4, ifindex_t, struct in_addr, + route_tag_t, uint32_t metric); +extern void ospf_external_info_delete(struct ospf *, uint8_t, unsigned short, + struct prefix_ipv4); +extern struct external_info *ospf_external_info_lookup(struct ospf *, uint8_t, + unsigned short, + struct prefix_ipv4 *); +extern void ospf_asbr_status_update(struct ospf *, uint8_t); +extern void ospf_schedule_asbr_redist_update(struct ospf *ospf); + +extern void ospf_redistribute_withdraw(struct ospf *, uint8_t, unsigned short); +extern void ospf_asbr_check(void); +extern void ospf_schedule_asbr_check(void); +extern void ospf_asbr_route_install_lsa(struct ospf_lsa *); +extern struct ospf_lsa *ospf_external_info_find_lsa(struct ospf *, + struct prefix_ipv4 *p); + +/* External Route Aggregator */ +extern void ospf_asbr_external_aggregator_init(struct ospf *instance); +extern void ospf_external_aggregator_free(struct ospf_external_aggr_rt *aggr); +extern bool is_valid_summary_addr(struct prefix_ipv4 *p); +extern struct ospf_external_aggr_rt * +ospf_external_aggr_match(struct ospf *ospf, struct prefix_ipv4 *p); +extern void ospf_unlink_ei_from_aggr(struct ospf *ospf, + struct ospf_external_aggr_rt *aggr, + struct external_info *ei); +extern struct ospf_lsa * +ospf_originate_summary_lsa(struct ospf *ospf, + struct ospf_external_aggr_rt *aggr, + struct external_info *ei); +extern int ospf_external_aggregator_timer_set(struct ospf *ospf, + uint16_t interval); +extern void ospf_external_aggrigator_free(struct ospf_external_aggr_rt *aggr); + +extern struct ospf_external_aggr_rt * +ospf_extrenal_aggregator_lookup(struct ospf *ospf, struct prefix_ipv4 *p); + +void ospf_unset_all_aggr_flag(struct ospf *ospf); + +extern int ospf_asbr_external_aggregator_set(struct ospf *ospf, + struct prefix_ipv4 *p, + route_tag_t tag); +extern int ospf_asbr_external_aggregator_unset(struct ospf *ospf, + struct prefix_ipv4 *p, + route_tag_t tag); +extern int ospf_asbr_external_rt_no_advertise(struct ospf *ospf, + struct prefix_ipv4 *p); +extern int ospf_asbr_external_rt_advertise(struct ospf *ospf, + struct prefix_ipv4 *p); +#endif /* _ZEBRA_OSPF_ASBR_H */ diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c new file mode 100644 index 0000000..610b5fc --- /dev/null +++ b/ospfd/ospf_ase.c @@ -0,0 +1,789 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF AS external route calculation. + * Copyright (C) 1999, 2000 Alex Zinin, Toshiaki Takada + */ + +#include + +#include "frrevent.h" +#include "memory.h" +#include "hash.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "vty.h" +#include "log.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_dump.h" + +struct ospf_route *ospf_find_asbr_route(struct ospf *ospf, + struct route_table *rtrs, + struct prefix_ipv4 *asbr) +{ + struct route_node *rn; + struct ospf_route * or, *best = NULL; + struct listnode *node; + struct list *chosen; + + /* Sanity check. */ + if (rtrs == NULL) + return NULL; + + rn = route_node_lookup(rtrs, (struct prefix *)asbr); + if (!rn) + return NULL; + + route_unlock_node(rn); + + chosen = list_new(); + + /* First try to find intra-area non-bb paths. */ + if (!CHECK_FLAG(ospf->config, OSPF_RFC1583_COMPATIBLE)) + for (ALL_LIST_ELEMENTS_RO((struct list *)rn->info, node, or)) + if (or->cost < OSPF_LS_INFINITY) + if (!OSPF_IS_AREA_ID_BACKBONE(or->u.std.area_id) + && + or->path_type == OSPF_PATH_INTRA_AREA) + listnode_add(chosen, or); + + /* If none is found -- look through all. */ + if (listcount(chosen) == 0) { + list_delete(&chosen); + chosen = rn->info; + } + + /* Now find the route with least cost. */ + for (ALL_LIST_ELEMENTS_RO(chosen, node, or)) + if (or->cost < OSPF_LS_INFINITY) { + if (best == NULL) + best = or ; + else if (best->cost > or->cost) + best = or ; + else if (best->cost == + or->cost + && IPV4_ADDR_CMP( + &best->u.std.area_id, + & or->u.std.area_id) + < 0) + best = or ; + } + + if (chosen != rn->info) + list_delete(&chosen); + + return best; +} + +struct ospf_route *ospf_find_asbr_route_through_area(struct route_table *rtrs, + struct prefix_ipv4 *asbr, + struct ospf_area *area) +{ + struct route_node *rn; + + /* Sanity check. */ + if (rtrs == NULL) + return NULL; + + rn = route_node_lookup(rtrs, (struct prefix *)asbr); + + if (rn) { + struct listnode *node; + struct ospf_route * or ; + + route_unlock_node(rn); + + for (ALL_LIST_ELEMENTS_RO((struct list *)rn->info, node, or)) + if (IPV4_ADDR_SAME(& or->u.std.area_id, &area->area_id)) + return or ; + } + + return NULL; +} + +static void ospf_ase_complete_direct_routes(struct ospf_route *ro, + struct in_addr nexthop) +{ + struct listnode *node; + struct ospf_path *op; + + for (ALL_LIST_ELEMENTS_RO(ro->paths, node, op)) + if (op->nexthop.s_addr == INADDR_ANY) + op->nexthop.s_addr = nexthop.s_addr; +} + +static int ospf_ase_forward_address_check(struct ospf *ospf, + struct in_addr fwd_addr) +{ + struct listnode *ifn; + struct ospf_interface *oi; + + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, ifn, oi)) + if (if_is_operative(oi->ifp)) + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + if (IPV4_ADDR_SAME(&oi->address->u.prefix4, + &fwd_addr)) + return 0; + + return 1; +} + +static struct ospf_route * +ospf_ase_calculate_new_route(struct ospf_lsa *lsa, + struct ospf_route *asbr_route, uint32_t metric) +{ + struct as_external_lsa *al; + struct ospf_route *new; + + al = (struct as_external_lsa *)lsa->data; + + new = ospf_route_new(); + + /* Set redistributed type -- does make sense? */ + /* new->type = type; */ + new->id = al->header.id; + new->mask = al->mask; + + if (!IS_EXTERNAL_METRIC(al->e[0].tos)) { + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug( + "Route[External]: type-1 created, asbr cost:%d metric:%d.", + asbr_route->cost, metric); + new->path_type = OSPF_PATH_TYPE1_EXTERNAL; + new->cost = asbr_route->cost + metric; /* X + Y */ + } else { + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug("Route[External]: type-2 created."); + new->path_type = OSPF_PATH_TYPE2_EXTERNAL; + new->cost = asbr_route->cost; /* X */ + new->u.ext.type2_cost = metric; /* Y */ + } + + new->type = OSPF_DESTINATION_NETWORK; + new->u.ext.origin = lsa; + new->u.ext.tag = ntohl(al->e[0].route_tag); + new->u.ext.asbr = asbr_route; + + assert(new != asbr_route); + + return new; +} + +#define OSPF_ASE_CALC_INTERVAL 1 + +int ospf_ase_calculate_route(struct ospf *ospf, struct ospf_lsa *lsa) +{ + uint32_t metric; + struct as_external_lsa *al; + struct ospf_route *asbr_route; + struct prefix_ipv4 asbr, p; + struct route_node *rn; + struct ospf_route *new, * or ; + int ret; + + assert(lsa); + al = (struct as_external_lsa *)lsa->data; + + if (lsa->data->type == OSPF_AS_NSSA_LSA) + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: Processing Type-7", __func__); + + /* Stay away from any Local Translated Type-7 LSAs */ + if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT)) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: Rejecting Local Xlt'd", __func__); + return 0; + } + + if (IS_DEBUG_OSPF(lsa, LSA)) { + zlog_debug( + "Route[External]: Calculate AS-external-LSA to %pI4/%d adv_router %pI4", + &al->header.id, ip_masklen(al->mask), + &al->header.adv_router); + } + + /* (1) If the cost specified by the LSA is LSInfinity, or if the + LSA's LS age is equal to MaxAge, then examine the next LSA. */ + if ((metric = GET_METRIC(al->e[0].metric)) >= OSPF_LS_INFINITY) { + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug( + "Route[External]: Metric is OSPF_LS_INFINITY"); + return 0; + } + if (IS_LSA_MAXAGE(lsa)) { + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug( + "Route[External]: AS-external-LSA is MAXAGE"); + return 0; + } + + /* (2) If the LSA was originated by the calculating router itself, + examine the next LSA. */ + if (IS_LSA_SELF(lsa)) { + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug( + "Route[External]: AS-external-LSA is self originated"); + return 0; + } + + /* (3) Call the destination described by the LSA N. N's address is + obtained by masking the LSA's Link State ID with the + network/subnet mask contained in the body of the LSA. Look + up the routing table entries (potentially one per attached + area) for the AS boundary router (ASBR) that originated the + LSA. If no entries exist for router ASBR (i.e., ASBR is + unreachable), do nothing with this LSA and consider the next + in the list. */ + + asbr.family = AF_INET; + asbr.prefix = al->header.adv_router; + asbr.prefixlen = IPV4_MAX_BITLEN; + apply_mask_ipv4(&asbr); + + asbr_route = ospf_find_asbr_route(ospf, ospf->new_rtrs, &asbr); + if (asbr_route == NULL) { + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug( + "Route[External]: Can't find originating ASBR route"); + return 0; + } + if (!(asbr_route->u.std.flags & ROUTER_LSA_EXTERNAL)) { + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug( + "Route[External]: Originating router is not an ASBR"); + return 0; + } + + /* Type-5 shouldn't be calculated if it is originated from NSSA ASBR. + * As per RFC 3101, expectation is to receive type-7 lsas from + * NSSA ASBR. Ignore calculation, if the current LSA is type-5 and + * originated ASBR's area is NSSA. + */ + if ((lsa->data->type == OSPF_AS_EXTERNAL_LSA) + && (asbr_route->u.std.external_routing != OSPF_AREA_DEFAULT)) { + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug( + "Route[External]: Ignore, If type-5 LSA from NSSA area."); + return 0; + } + + /* Else, this LSA describes an AS external path to destination + N. Examine the forwarding address specified in the AS- + external-LSA. This indicates the IP address to which + packets for the destination should be forwarded. */ + + if (al->e[0].fwd_addr.s_addr == INADDR_ANY) { + /* If the forwarding address is set to 0.0.0.0, packets should + be sent to the ASBR itself. Among the multiple routing table + entries for the ASBR, select the preferred entry as follows. + If RFC1583Compatibility is set to "disabled", prune the set + of routing table entries for the ASBR as described in + Section 16.4.1. In any case, among the remaining routing + table entries, select the routing table entry with the least + cost; when there are multiple least cost routing table + entries the entry whose associated area has the largest OSPF + Area ID (when considered as an unsigned 32-bit integer) is + chosen. */ + + /* asbr_route already contains the requested route */ + } else { + /* If the forwarding address is non-zero, look up the + forwarding address in the routing table.[24] The matching + routing table entry must specify an intra-area or inter-area + path; if no such path exists, do nothing with the LSA and + consider the next in the list. */ + if (!ospf_ase_forward_address_check(ospf, al->e[0].fwd_addr)) { + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug( + "Route[External]: Forwarding address is our router address"); + return 0; + } + + asbr.family = AF_INET; + asbr.prefix = al->e[0].fwd_addr; + asbr.prefixlen = IPV4_MAX_BITLEN; + + rn = route_node_match(ospf->new_table, (struct prefix *)&asbr); + + if (rn == NULL || (asbr_route = rn->info) == NULL) { + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug( + "Route[External]: Can't find route to forwarding address"); + if (rn) + route_unlock_node(rn); + return 0; + } + + route_unlock_node(rn); + } + + /* (4) Let X be the cost specified by the preferred routing table + entry for the ASBR/forwarding address, and Y the cost + specified in the LSA. X is in terms of the link state + metric, and Y is a type 1 or 2 external metric. */ + + + /* (5) Look up the routing table entry for the destination N. If + no entry exists for N, install the AS external path to N, + with next hop equal to the list of next hops to the + forwarding address, and advertising router equal to ASBR. + If the external metric type is 1, then the path-type is set + to type 1 external and the cost is equal to X+Y. If the + external metric type is 2, the path-type is set to type 2 + external, the link state component of the route's cost is X, + and the type 2 cost is Y. */ + new = ospf_ase_calculate_new_route(lsa, asbr_route, metric); + + /* (6) Compare the AS external path described by the LSA with the + existing paths in N's routing table entry, as follows. If + the new path is preferred, it replaces the present paths in + N's routing table entry. If the new path is of equal + preference, it is added to N's routing table entry's list of + paths. */ + + /* Set prefix. */ + p.family = AF_INET; + p.prefix = al->header.id; + p.prefixlen = ip_masklen(al->mask); + + /* if there is a Intra/Inter area route to the N + do not install external route */ + if ((rn = route_node_lookup(ospf->new_table, (struct prefix *)&p))) { + route_unlock_node(rn); + if (rn->info == NULL) + zlog_info("Route[External]: rn->info NULL"); + if (new) + ospf_route_free(new); + return 0; + } + /* Find a route to the same dest */ + /* If there is no route, create new one. */ + if ((rn = route_node_lookup(ospf->new_external_route, + (struct prefix *)&p))) + route_unlock_node(rn); + + if (!rn || (or = rn->info) == NULL) { + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug("Route[External]: Adding a new route %pFX with paths %u", + &p, listcount(asbr_route->paths)); + + ospf_route_add(ospf->new_external_route, &p, new, asbr_route); + + if (al->e[0].fwd_addr.s_addr != INADDR_ANY) + ospf_ase_complete_direct_routes(new, al->e[0].fwd_addr); + return 0; + } else { + /* (a) Intra-area and inter-area paths are always preferred + over AS external paths. + + (b) Type 1 external paths are always preferred over type 2 + external paths. When all paths are type 2 external + paths, the paths with the smallest advertised type 2 + metric are always preferred. */ + ret = ospf_route_cmp(ospf, new, or); + + /* (c) If the new AS external path is still + indistinguishable + from the current paths in the N's routing table + entry, + and RFC1583Compatibility is set to "disabled", select + the preferred paths based on the intra-AS paths to + the + ASBR/forwarding addresses, as specified in Section + 16.4.1. + + (d) If the new AS external path is still + indistinguishable + from the current paths in the N's routing table + entry, + select the preferred path based on a least cost + comparison. Type 1 external paths are compared by + looking at the sum of the distance to the forwarding + address and the advertised type 1 metric (X+Y). Type + 2 + external paths advertising equal type 2 metrics are + compared by looking at the distance to the forwarding + addresses. + */ + /* New route is better */ + if (ret < 0) { + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug( + "Route[External]: New route is better"); + ospf_route_subst(rn, new, asbr_route); + if (al->e[0].fwd_addr.s_addr != INADDR_ANY) + ospf_ase_complete_direct_routes( + new, al->e[0].fwd_addr); + or = new; + new = NULL; + } + /* Old route is better */ + else if (ret > 0) { + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug( + "Route[External]: Old route is better"); + /* do nothing */ + } + /* Routes are equal */ + else { + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug("Route[External]: Routes are equal"); + ospf_route_copy_nexthops(or, asbr_route->paths); + if (al->e[0].fwd_addr.s_addr != INADDR_ANY) + ospf_ase_complete_direct_routes( + or, al->e[0].fwd_addr); + } + } + /* Make sure setting newly calculated ASBR route.*/ + or->u.ext.asbr = asbr_route; + if (new) + ospf_route_free(new); + + lsa->route = or ; + return 0; +} + +static int ospf_ase_route_match_same(struct route_table *rt, + struct prefix *prefix, + struct ospf_route *newor) +{ + struct route_node *rn; + struct ospf_route *or; + struct ospf_path *op; + struct ospf_path *newop; + struct listnode *n1; + struct listnode *n2; + + if (!rt || !prefix) + return 0; + + rn = route_node_lookup(rt, prefix); + if (!rn) + return 0; + + route_unlock_node(rn); + + or = rn->info; + + assert(or); + + if (or->path_type != newor->path_type) + return 0; + + switch (or->path_type) { + case OSPF_PATH_TYPE1_EXTERNAL: + if (or->cost != newor->cost) + return 0; + break; + case OSPF_PATH_TYPE2_EXTERNAL: + if ((or->cost != newor->cost) + || (or->u.ext.type2_cost != newor->u.ext.type2_cost)) + return 0; + break; + default: + assert(0); + return 0; + } + + assert(or->paths); + + if (or->paths->count != newor->paths->count) + return 0; + + /* Check each path. */ + for (n1 = listhead(or->paths), n2 = listhead(newor->paths); n1 && n2; + n1 = listnextnode_unchecked(n1), n2 = listnextnode_unchecked(n2)) { + op = listgetdata(n1); + newop = listgetdata(n2); + + if (!IPV4_ADDR_SAME(&op->nexthop, &newop->nexthop)) + return 0; + if (op->ifindex != newop->ifindex) + return 0; + } + + if (or->u.ext.tag != newor->u.ext.tag) + return 0; + + return 1; +} + +static int ospf_ase_compare_tables(struct ospf *ospf, + struct route_table *new_external_route, + struct route_table *old_external_route) +{ + struct route_node *rn, *new_rn; + struct ospf_route * or ; + + /* Remove deleted routes */ + for (rn = route_top(old_external_route); rn; rn = route_next(rn)) + if ((or = rn->info)) { + if (!(new_rn = route_node_lookup(new_external_route, + &rn->p))) + ospf_zebra_delete( + ospf, (struct prefix_ipv4 *)&rn->p, or); + else + route_unlock_node(new_rn); + } + + + /* Install new routes */ + for (rn = route_top(new_external_route); rn; rn = route_next(rn)) + if ((or = rn->info) != NULL) + if (!ospf_ase_route_match_same(old_external_route, + &rn->p, or)) + ospf_zebra_add( + ospf, (struct prefix_ipv4 *)&rn->p, or); + + return 0; +} + +static void ospf_ase_calculate_timer(struct event *t) +{ + struct ospf *ospf; + struct ospf_lsa *lsa; + struct route_node *rn; + struct listnode *node; + struct ospf_area *area; + struct timeval start_time, stop_time; + + ospf = EVENT_ARG(t); + ospf->t_ase_calc = NULL; + + if (ospf->ase_calc) { + ospf->ase_calc = 0; + + monotime(&start_time); + + /* Calculate external route for each AS-external-LSA */ + LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) + ospf_ase_calculate_route(ospf, lsa); + + /* This version simple adds to the table all NSSA areas */ + if (ospf->anyNSSA) + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: looking at area %pI4", + __func__, &area->area_id); + + if (area->external_routing == OSPF_AREA_NSSA) + LSDB_LOOP (NSSA_LSDB(area), rn, lsa) + ospf_ase_calculate_route(ospf, + lsa); + } + /* kevinm: And add the NSSA routes in ospf_top */ + LSDB_LOOP (NSSA_LSDB(ospf), rn, lsa) + ospf_ase_calculate_route(ospf, lsa); + + /* Compare old and new external routing table and install the + difference info zebra/kernel */ + ospf_ase_compare_tables(ospf, ospf->new_external_route, + ospf->old_external_route); + + /* Delete old external routing table */ + ospf_route_table_free(ospf->old_external_route); + ospf->old_external_route = ospf->new_external_route; + ospf->new_external_route = route_table_init(); + + monotime(&stop_time); + + if (IS_DEBUG_OSPF_EVENT) + zlog_info( + "SPF Processing Time(usecs): External Routes: %lld", + (stop_time.tv_sec - start_time.tv_sec) + * 1000000LL + + (stop_time.tv_usec + - start_time.tv_usec)); + } + + /* + * Uninstall remnant routes that were installed before the restart, but + * that are no longer valid. + */ + if (ospf->gr_info.finishing_restart) { + ospf_zebra_gr_disable(ospf); + ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period); + ospf->gr_info.finishing_restart = false; + } +} + +void ospf_ase_calculate_schedule(struct ospf *ospf) +{ + if (ospf == NULL) + return; + + ospf->ase_calc = 1; +} + +void ospf_ase_calculate_timer_add(struct ospf *ospf) +{ + if (ospf == NULL) + return; + + event_add_timer(master, ospf_ase_calculate_timer, ospf, + OSPF_ASE_CALC_INTERVAL, &ospf->t_ase_calc); +} + +void ospf_ase_register_external_lsa(struct ospf_lsa *lsa, struct ospf *top) +{ + struct route_node *rn; + struct prefix_ipv4 p; + struct list *lst; + struct as_external_lsa *al; + + al = (struct as_external_lsa *)lsa->data; + p.family = AF_INET; + p.prefix = lsa->data->id; + p.prefixlen = ip_masklen(al->mask); + apply_mask_ipv4(&p); + + rn = route_node_get(top->external_lsas, (struct prefix *)&p); + if ((lst = rn->info) == NULL) + rn->info = lst = list_new(); + else + route_unlock_node(rn); + + /* We assume that if LSA is deleted from DB + is is also deleted from this RT */ + listnode_add(lst, ospf_lsa_lock(lsa)); /* external_lsas lst */ +} + +void ospf_ase_unregister_external_lsa(struct ospf_lsa *lsa, struct ospf *top) +{ + struct route_node *rn; + struct prefix_ipv4 p; + struct list *lst; + struct as_external_lsa *al; + + al = (struct as_external_lsa *)lsa->data; + p.family = AF_INET; + p.prefix = lsa->data->id; + p.prefixlen = ip_masklen(al->mask); + apply_mask_ipv4(&p); + + rn = route_node_lookup(top->external_lsas, (struct prefix *)&p); + + if (rn) { + lst = rn->info; + struct listnode *node = listnode_lookup(lst, lsa); + /* Unlock lsa only if node is present in the list */ + if (node) { + listnode_delete(lst, lsa); + ospf_lsa_unlock(&lsa); /* external_lsas list */ + } + + route_unlock_node(rn); + } +} + +void ospf_ase_external_lsas_finish(struct route_table *rt) +{ + struct route_node *rn; + struct ospf_lsa *lsa; + struct list *lst; + struct listnode *node, *nnode; + + for (rn = route_top(rt); rn; rn = route_next(rn)) + if ((lst = rn->info) != NULL) { + for (ALL_LIST_ELEMENTS(lst, node, nnode, lsa)) + ospf_lsa_unlock(&lsa); /* external_lsas lst */ + list_delete(&lst); + } + + route_table_finish(rt); +} + +void ospf_ase_incremental_update(struct ospf *ospf, struct ospf_lsa *lsa) +{ + struct list *lsas; + struct listnode *node; + struct route_node *rn, *rn2; + struct prefix_ipv4 p; + struct route_table *tmp_old; + struct as_external_lsa *al; + + al = (struct as_external_lsa *)lsa->data; + p.family = AF_INET; + p.prefix = lsa->data->id; + p.prefixlen = ip_masklen(al->mask); + apply_mask_ipv4(&p); + + /* if new_table is NULL, there was no spf calculation, thus + incremental update is unneeded */ + if (!ospf->new_table) + return; + + /* If there is already an intra-area or inter-area route + to the destination, no recalculation is necessary + (internal routes take precedence). */ + + rn = route_node_lookup(ospf->new_table, (struct prefix *)&p); + if (rn) { + route_unlock_node(rn); + if (rn->info) + return; + } + + rn = route_node_lookup(ospf->external_lsas, (struct prefix *)&p); + assert(rn); + assert(rn->info); + lsas = rn->info; + route_unlock_node(rn); + + for (ALL_LIST_ELEMENTS_RO(lsas, node, lsa)) + ospf_ase_calculate_route(ospf, lsa); + + /* prepare temporary old routing table for compare */ + tmp_old = route_table_init(); + rn = route_node_lookup(ospf->old_external_route, (struct prefix *)&p); + if (rn && rn->info) { + rn2 = route_node_get(tmp_old, (struct prefix *)&p); + rn2->info = rn->info; + route_unlock_node(rn); + } + + /* install changes to zebra */ + ospf_ase_compare_tables(ospf, ospf->new_external_route, tmp_old); + + /* update ospf->old_external_route table */ + if (rn && rn->info) + ospf_route_free((struct ospf_route *)rn->info); + + rn2 = route_node_lookup(ospf->new_external_route, (struct prefix *)&p); + /* if new route exists, install it to ospf->old_external_route */ + if (rn2 && rn2->info) { + if (!rn) + rn = route_node_get(ospf->old_external_route, + (struct prefix *)&p); + rn->info = rn2->info; + } else { + /* remove route node from ospf->old_external_route */ + if (rn) { + rn->info = NULL; + route_unlock_node(rn); + } + } + + if (rn2) { + /* rn2->info is stored in route node of ospf->old_external_route + */ + rn2->info = NULL; + route_unlock_node(rn2); + route_unlock_node(rn2); + } + + route_table_finish(tmp_old); +} diff --git a/ospfd/ospf_ase.h b/ospfd/ospf_ase.h new file mode 100644 index 0000000..d59a470 --- /dev/null +++ b/ospfd/ospf_ase.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF AS External route calculation. + * Copyright (C) 1999, 2000 Alex Zinin, Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPF_ASE_H +#define _ZEBRA_OSPF_ASE_H + +extern struct ospf_route * +ospf_find_asbr_route(struct ospf *, struct route_table *, struct prefix_ipv4 *); +extern struct ospf_route * +ospf_find_asbr_route_through_area(struct route_table *, struct prefix_ipv4 *, + struct ospf_area *); + +extern int ospf_ase_calculate_route(struct ospf *, struct ospf_lsa *); +extern void ospf_ase_calculate_schedule(struct ospf *); +extern void ospf_ase_calculate_timer_add(struct ospf *); + +extern void ospf_ase_external_lsas_finish(struct route_table *); +extern void ospf_ase_incremental_update(struct ospf *, struct ospf_lsa *); +extern void ospf_ase_register_external_lsa(struct ospf_lsa *, struct ospf *); +extern void ospf_ase_unregister_external_lsa(struct ospf_lsa *, struct ospf *); + +#endif /* _ZEBRA_OSPF_ASE_H */ diff --git a/ospfd/ospf_auth.c b/ospfd/ospf_auth.c new file mode 100644 index 0000000..11ee1dd --- /dev/null +++ b/ospfd/ospf_auth.c @@ -0,0 +1,716 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 Amnesh Inc. + * Mahdi Varasteh + */ + +#include + +#include "linklist.h" +#include "if.h" +#include "checksum.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_errors.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_auth.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_network.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_bfd.h" +#include "ospfd/ospf_gr.h" +#ifdef CRYPTO_INTERNAL +#include "sha256.h" +#include "md5.h" +#endif + +const uint8_t ospf_auth_apad[KEYCHAIN_MAX_HASH_SIZE] = { + 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, + 0xF3, 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, + 0xE1, 0xF3, 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, 0xF3, 0x87, + 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, 0xF3, + 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, + 0xF3, 0x87, 0x8F, 0xE1, 0xF3, 0x87, 0x8F, 0xE1, 0xF3 +}; + +static int ospf_check_sum(struct ospf_header *ospfh) +{ + uint32_t ret; + uint16_t sum; + + /* clear auth_data for checksum. */ + memset(ospfh->u.auth_data, 0, OSPF_AUTH_SIMPLE_SIZE); + + /* keep checksum and clear. */ + sum = ospfh->checksum; + memset(&ospfh->checksum, 0, sizeof(uint16_t)); + + /* calculate checksum. */ + ret = in_cksum(ospfh, ntohs(ospfh->length)); + + if (ret != sum) { + zlog_info("%s: checksum mismatch, my %X, his %X", __func__, ret, + sum); + return 0; + } + + return 1; +} + +#ifdef CRYPTO_OPENSSL +static const EVP_MD *ospf_auth_get_openssl_evp_md_from_key(struct key *key) +{ + if (key->hash_algo == KEYCHAIN_ALGO_HMAC_SHA1) + return EVP_get_digestbyname("sha1"); + else if (key->hash_algo == KEYCHAIN_ALGO_HMAC_SHA256) + return EVP_get_digestbyname("sha256"); + else if (key->hash_algo == KEYCHAIN_ALGO_HMAC_SHA384) + return EVP_get_digestbyname("sha384"); + else if (key->hash_algo == KEYCHAIN_ALGO_HMAC_SHA512) + return EVP_get_digestbyname("sha512"); + return NULL; +} +#endif + +static int ospf_auth_check_hmac_sha_digest(struct ospf_interface *oi, + struct ospf_header *ospfh, + struct ip *iph, + struct key *key) +{ + unsigned char digest[KEYCHAIN_MAX_HASH_SIZE]; + struct ospf_neighbor *nbr; + uint16_t length = ntohs(ospfh->length); + uint16_t hash_length = keychain_get_hash_len(key->hash_algo); +#ifdef CRYPTO_OPENSSL + uint32_t openssl_hash_length = hash_length; + HMAC_CTX *ctx; + const EVP_MD *md_alg = ospf_auth_get_openssl_evp_md_from_key(key); + + if (!md_alg) { + flog_warn(EC_OSPF_AUTH, + "interface %s: invalid HMAC algorithm, Router-ID: %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + } +#elif CRYPTO_INTERNAL + HMAC_SHA256_CTX ctx; + + if (key->hash_algo != KEYCHAIN_ALGO_HMAC_SHA256) { + flog_warn(EC_OSPF_AUTH, + "interface %s: HMAC algorithm not supported, Router-ID: %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + } +#endif + /* check crypto seqnum. */ + nbr = ospf_nbr_lookup(oi, iph, ospfh); + + if (nbr && + ntohl(nbr->crypt_seqnum) > ntohl(ospfh->u.crypt.crypt_seqnum)) { + flog_warn(EC_OSPF_AUTH, + "interface %s: ospf_check_hmac_sha bad sequence %u (expect %d), Router-ID: %pI4", + IF_NAME(oi), ntohl(ospfh->u.crypt.crypt_seqnum), + ntohl(nbr->crypt_seqnum), &ospfh->router_id); + return 0; + } +#ifdef CRYPTO_OPENSSL + ctx = HMAC_CTX_new(); + HMAC_Init_ex(ctx, key->string, strlen(key->string), md_alg, NULL); + HMAC_Update(ctx, (const unsigned char *)ospfh, length); + HMAC_Update(ctx, (const unsigned char *)ospf_auth_apad, hash_length); + HMAC_Final(ctx, digest, &openssl_hash_length); + HMAC_CTX_free(ctx); +#elif CRYPTO_INTERNAL + memset(&ctx, 0, sizeof(ctx)); + HMAC__SHA256_Init(&ctx, key->string, strlen(key->string)); + HMAC__SHA256_Update(&ctx, ospfh, length); + HMAC__SHA256_Update(&ctx, ospf_auth_apad, hash_length); + HMAC__SHA256_Final(digest, &ctx); +#endif + if (memcmp((caddr_t)ospfh + length, digest, hash_length)) { + flog_warn(EC_OSPF_AUTH, + "interface %s: ospf_check_hmac_sha checksum mismatch %u, Router-ID: %pI4", + IF_NAME(oi), length, &ospfh->router_id); + return 0; + } + if (nbr) + nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum; + return 1; +} + +static int ospf_auth_check_md5_digest(struct ospf_interface *oi, + struct ospf_header *ospfh, struct ip *iph, struct key *key) +{ +#ifdef CRYPTO_OPENSSL + EVP_MD_CTX *ctx; +#elif CRYPTO_INTERNAL + MD5_CTX ctx; +#endif + char auth_key[OSPF_AUTH_MD5_SIZE + 1]; + unsigned char digest[OSPF_AUTH_MD5_SIZE]; + struct ospf_neighbor *nbr; + struct crypt_key *ck = NULL; + uint16_t length = ntohs(ospfh->length); + + if (length < sizeof(struct ospf_header)) {/* for coverity's sake */ + flog_warn(EC_OSPF_AUTH, + "%s: Invalid packet length of %u received on interface %s, Router-ID: %pI4", + __func__, length, IF_NAME(oi), &ospfh->router_id); + return 0; + } + + if (key == NULL) { + ck = ospf_crypt_key_lookup(OSPF_IF_PARAM(oi, auth_crypt), + ospfh->u.crypt.key_id); + if (ck == NULL) { + flog_warn( + EC_OSPF_AUTH, + "interface %s: %s no key %d, Router-ID: %pI4", + IF_NAME(oi), __func__, ospfh->u.crypt.key_id, &ospfh->router_id); + return 0; + } + } + /* check crypto seqnum. */ + nbr = ospf_nbr_lookup(oi, iph, ospfh); + + if (nbr && + ntohl(nbr->crypt_seqnum) > ntohl(ospfh->u.crypt.crypt_seqnum)) { + flog_warn(EC_OSPF_AUTH, + "interface %s: %s bad sequence %d (expect %d), Router-ID: %pI4", + IF_NAME(oi), __func__, ntohl(ospfh->u.crypt.crypt_seqnum), + ntohl(nbr->crypt_seqnum), &ospfh->router_id); + return 0; + } + + memset(auth_key, 0, OSPF_AUTH_MD5_SIZE + 1); + if (ck == NULL) + strlcpy(auth_key, key->string, OSPF_AUTH_MD5_SIZE + 1); + else + strlcpy(auth_key, (char *)ck->auth_key, OSPF_AUTH_MD5_SIZE + 1); + /* Generate a digest for the ospf packet - their digest + our digest. */ +#ifdef CRYPTO_OPENSSL + uint32_t md5_size = OSPF_AUTH_MD5_SIZE; + + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_md5()); + EVP_DigestUpdate(ctx, ospfh, length); + EVP_DigestUpdate(ctx, auth_key, OSPF_AUTH_MD5_SIZE); + EVP_DigestFinal(ctx, digest, &md5_size); + EVP_MD_CTX_free(ctx); +#elif CRYPTO_INTERNAL + memset(&ctx, 0, sizeof(ctx)); + MD5Init(&ctx); + MD5Update(&ctx, ospfh, length); + MD5Update(&ctx, auth_key, OSPF_AUTH_MD5_SIZE); + MD5Final(digest, &ctx); +#endif + + /* compare the two */ + if (memcmp((caddr_t)ospfh + length, digest, OSPF_AUTH_MD5_SIZE)) { + flog_warn(EC_OSPF_AUTH, + "interface %s: ospf_check_md5 checksum mismatch, Router-ID: %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + } + + /* save neighbor's crypt_seqnum */ + if (nbr) + nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum; + return 1; +} + +static int ospf_auth_make_md5_digest(struct ospf_interface *oi, + struct ospf_packet *op, struct key *key) +{ + void *ibuf = STREAM_DATA(op->s); + struct ospf_header *ospfh = (struct ospf_header *)ibuf; + unsigned char digest[OSPF_AUTH_MD5_SIZE]; + uint16_t length = ntohs(ospfh->length); +#ifdef CRYPTO_OPENSSL + EVP_MD_CTX *ctx; +#elif CRYPTO_INTERNAL + MD5_CTX ctx; +#endif + char auth_key[OSPF_AUTH_MD5_SIZE + 1]; + + if ((length < (sizeof(struct ospf_header))) || (length > op->length)) { /* for coverity's sake */ + flog_warn(EC_OSPF_AUTH, + "%s: Invalid packet length of %u received on interface %s, Router-ID: %pI4", + __func__, length, IF_NAME(oi), &ospfh->router_id); + return 0; + } + + memset(auth_key, 0, OSPF_AUTH_MD5_SIZE + 1); + strlcpy(auth_key, key->string, OSPF_AUTH_MD5_SIZE + 1); + /* Generate a digest for the ospf packet - their digest + our digest. */ +#ifdef CRYPTO_OPENSSL + uint32_t md5_size = OSPF_AUTH_MD5_SIZE; + + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_md5()); + EVP_DigestUpdate(ctx, ospfh, length); + EVP_DigestUpdate(ctx, auth_key, OSPF_AUTH_MD5_SIZE); + EVP_DigestFinal(ctx, digest, &md5_size); + EVP_MD_CTX_free(ctx); +#elif CRYPTO_INTERNAL + memset(&ctx, 0, sizeof(ctx)); + MD5Init(&ctx); + MD5Update(&ctx, ospfh, length); + MD5Update(&ctx, auth_key, OSPF_AUTH_MD5_SIZE); + MD5Final(digest, &ctx); +#endif + + stream_put(op->s, digest, OSPF_AUTH_MD5_SIZE); + + op->length = ntohs(ospfh->length) + OSPF_AUTH_MD5_SIZE; + + if (stream_get_endp(op->s) != op->length) + /* XXX size_t */ + flog_warn(EC_OSPF_AUTH, + "%s: length mismatch stream %lu ospf_packet %u, Router-ID %pI4", + __func__, (unsigned long)stream_get_endp(op->s), + op->length, &ospfh->router_id); + + return OSPF_AUTH_MD5_SIZE; +} + +static int ospf_auth_make_hmac_sha_digest(struct ospf_interface *oi, + struct ospf_packet *op, + struct key *key) +{ + void *ibuf; + struct ospf_header *ospfh; + unsigned char digest[KEYCHAIN_MAX_HASH_SIZE] = { 0 }; + uint16_t hash_length = keychain_get_hash_len(key->hash_algo); + + ibuf = STREAM_DATA(op->s); + ospfh = (struct ospf_header *)ibuf; +#ifdef CRYPTO_OPENSSL + uint32_t openssl_hash_length = hash_length; + HMAC_CTX *ctx; + const EVP_MD *md_alg = ospf_auth_get_openssl_evp_md_from_key(key); + + if (!md_alg) { + flog_warn(EC_OSPF_AUTH, + "interface %s: invalid HMAC algorithm, Router-ID: %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + } +#elif CRYPTO_INTERNAL + HMAC_SHA256_CTX ctx; + + if (key->hash_algo != KEYCHAIN_ALGO_HMAC_SHA256) { + flog_warn(EC_OSPF_AUTH, + "interface %s: HMAC algorithm not supported, Router-ID: %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + } +#endif +#ifdef CRYPTO_OPENSSL + ctx = HMAC_CTX_new(); + HMAC_Init_ex(ctx, key->string, strlen(key->string), md_alg, NULL); + HMAC_Update(ctx, (const unsigned char *)ospfh, ntohs(ospfh->length)); + HMAC_Update(ctx, (const unsigned char *)ospf_auth_apad, hash_length); + HMAC_Final(ctx, digest, &openssl_hash_length); + HMAC_CTX_free(ctx); +#elif CRYPTO_INTERNAL + memset(&ctx, 0, sizeof(ctx)); + HMAC__SHA256_Init(&ctx, key->string, strlen(key->string)); + HMAC__SHA256_Update(&ctx, ospfh, ntohs(ospfh->length)); + HMAC__SHA256_Update(&ctx, ospf_auth_apad, hash_length); + HMAC__SHA256_Final(digest, &ctx); +#endif + stream_put(op->s, digest, hash_length); + + op->length = ntohs(ospfh->length) + hash_length; + + if (stream_get_endp(op->s) != op->length) + /* XXX size_t */ + flog_warn(EC_OSPF_AUTH, + "%s: length mismatch stream %lu ospf_packet %u, Router-ID %pI4", + __func__, (unsigned long)stream_get_endp(op->s), + op->length, &ospfh->router_id); + + return hash_length; +} + +int ospf_auth_check_digest(struct ospf_interface *oi, struct ip *iph, struct ospf_header *ospfh) +{ + struct keychain *keychain = NULL; + struct key *key = NULL; + int key_id = ospfh->u.crypt.key_id; + uint8_t auth_data_len = ospfh->u.crypt.auth_data_len; + + if (!OSPF_IF_PARAM(oi, keychain_name)) + return ospf_auth_check_md5_digest(oi, ospfh, iph, NULL); + + keychain = keychain_lookup(OSPF_IF_PARAM(oi, keychain_name)); + if (!keychain) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn(EC_OSPF_AUTH, + "interface %s: Keychain %s is not available, Router-ID %pI4", + IF_NAME(oi), OSPF_IF_PARAM(oi, keychain_name), + &ospfh->router_id); + return 0; + } + + key = key_lookup_for_accept(keychain, key_id); + if (!key) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn(EC_OSPF_AUTH, + "interface %s: Key ID %d not found in keychain %s, Router-ID %pI4", + IF_NAME(oi), key_id, keychain->name, + &ospfh->router_id); + return 0; + } + + if (key->string == NULL || key->hash_algo == KEYCHAIN_ALGO_NULL) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn(EC_OSPF_AUTH, + "interface %s: Key ID %d in keychain %s is incomplete, Router-ID %pI4", + IF_NAME(oi), key_id, keychain->name, + &ospfh->router_id); + return 0; + } + + if (keychain_get_hash_len(key->hash_algo) != auth_data_len) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn(EC_OSPF_AUTH, + "interface %s: Key ID %d in keychain %s hash length mismatch, Router-ID %pI4", + IF_NAME(oi), key_id, keychain->name, + &ospfh->router_id); + return 0; + } + + /* Backward compatibility with RFC 2328 keyed-MD5 authentication */ + if (key->hash_algo == KEYCHAIN_ALGO_MD5) + return ospf_auth_check_md5_digest(oi, ospfh, iph, key); + + return ospf_auth_check_hmac_sha_digest(oi, ospfh, iph, key); +} + +int ospf_auth_make_digest(struct ospf_interface *oi, struct ospf_packet *op) +{ + struct ospf_header *ospfh; + void *ibuf; + struct keychain *keychain = NULL; + struct key *key = NULL; + int key_id; + + ibuf = STREAM_DATA(op->s); + ospfh = (struct ospf_header *)ibuf; + + key_id = ospfh->u.crypt.key_id; + + if (!OSPF_IF_PARAM(oi, keychain_name)) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn(EC_OSPF_AUTH, + "interface %s: Keychain is not set, Router-ID %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + } + + keychain = oi->keychain; + if (!keychain) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, SEND)) + flog_warn(EC_OSPF_AUTH, + "interface %s: Keychain %s is not available to send, Router-ID %pI4", + IF_NAME(oi), OSPF_IF_PARAM(oi, keychain_name), + &ospfh->router_id); + return 0; + } + + key = oi->key; + if (!key) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, SEND)) + flog_warn(EC_OSPF_AUTH, + "interface %s: Key ID %d not found in keychain %s, Router-ID %pI4", + IF_NAME(oi), key_id, keychain->name, + &ospfh->router_id); + return 0; + } + + if (key->string == NULL || key->hash_algo == KEYCHAIN_ALGO_NULL) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, SEND)) + flog_warn(EC_OSPF_AUTH, + "interface %s: Key ID %d in keychain %s is incomplete, Router-ID %pI4", + IF_NAME(oi), key_id, keychain->name, + &ospfh->router_id); + return 0; + } + + /* Backward compatibility with RFC 2328 keyed-MD5 authentication */ + if (key->hash_algo == KEYCHAIN_ALGO_MD5) + return ospf_auth_make_md5_digest(oi, op, key); + else + return ospf_auth_make_hmac_sha_digest(oi, op, key); +} + +/* This function is called from ospf_write(), it will detect the + * authentication scheme and if it is MD5, it will change the sequence + * and update the MD5 digest. + */ + +int ospf_auth_make(struct ospf_interface *oi, struct ospf_packet *op) +{ + struct ospf_header *ospfh; + unsigned char digest[OSPF_AUTH_MD5_SIZE] = {0}; +#ifdef CRYPTO_OPENSSL + EVP_MD_CTX *ctx; +#elif CRYPTO_INTERNAL + MD5_CTX ctx; +#endif + void *ibuf; + uint32_t t; + struct crypt_key *ck; + const uint8_t *auth_key = NULL; + + ibuf = STREAM_DATA(op->s); + ospfh = (struct ospf_header *)ibuf; + + if (ntohs(ospfh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC) + return 0; + + /* We do this here so when we dup a packet, we don't have to + * waste CPU rewriting other headers. + + Note that frr_time /deliberately/ is not used here. + */ + t = (time(NULL) & 0xFFFFFFFF); + if (t > oi->crypt_seqnum) + oi->crypt_seqnum = t; + else + oi->crypt_seqnum++; + + ospfh->u.crypt.crypt_seqnum = htonl(oi->crypt_seqnum); + + /* Get MD5 Authentication key from auth_key list. */ + if (list_isempty(OSPF_IF_PARAM(oi, auth_crypt)) && OSPF_IF_PARAM(oi, keychain_name) == NULL) + auth_key = (const uint8_t *)digest; + else if (!list_isempty(OSPF_IF_PARAM(oi, auth_crypt))) { + ck = listgetdata(listtail(OSPF_IF_PARAM(oi, auth_crypt))); + auth_key = ck->auth_key; + } + + if (auth_key) { + /* Generate a digest for the entire packet + our secret key. */ +#ifdef CRYPTO_OPENSSL + uint32_t md5_size = OSPF_AUTH_MD5_SIZE; + + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_md5()); + EVP_DigestUpdate(ctx, ibuf, ntohs(ospfh->length)); + EVP_DigestUpdate(ctx, auth_key, OSPF_AUTH_MD5_SIZE); + EVP_DigestFinal(ctx, digest, &md5_size); + EVP_MD_CTX_free(ctx); +#elif CRYPTO_INTERNAL + memset(&ctx, 0, sizeof(ctx)); + MD5Init(&ctx); + MD5Update(&ctx, ibuf, ntohs(ospfh->length)); + MD5Update(&ctx, auth_key, OSPF_AUTH_MD5_SIZE); + MD5Final(digest, &ctx); +#endif + + /* Append md5 digest to the end of the stream. */ + stream_put(op->s, digest, OSPF_AUTH_MD5_SIZE); + + /* We do *NOT* increment the OSPF header length. */ + op->length = ntohs(ospfh->length) + OSPF_AUTH_MD5_SIZE; + + if (stream_get_endp(op->s) != op->length) + /* XXX size_t */ + flog_warn( + EC_OSPF_AUTH, + "%s: length mismatch stream %lu ospf_packet %u, Router-ID %pI4", + __func__, (unsigned long)stream_get_endp(op->s), + op->length, &ospfh->router_id); + + return OSPF_AUTH_MD5_SIZE; + } else + return ospf_auth_make_digest(oi, op); +} + +/* Return 1, if the packet is properly authenticated and checksummed, + * 0 otherwise. In particular, check that AuType header field is valid and + * matches the locally configured AuType, and that D.5 requirements are met. + */ +int ospf_auth_check(struct ospf_interface *oi, struct ip *iph, + struct ospf_header *ospfh) +{ + uint16_t iface_auth_type; + uint16_t pkt_auth_type = ntohs(ospfh->auth_type); + + iface_auth_type = ospf_auth_type(oi); + + switch (pkt_auth_type) { + case OSPF_AUTH_NULL: /* RFC2328 D.5.1 */ + if (iface_auth_type != OSPF_AUTH_NULL) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_PACKET, + "interface %s: auth-type mismatch, local %s, rcvd Null, Router-ID %pI4", + IF_NAME(oi), + lookup_msg(ospf_auth_type_str, + iface_auth_type, NULL), + &ospfh->router_id); + return 0; + } + if (!ospf_check_sum(ospfh)) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_PACKET, + "interface %s: Null auth OK, but checksum error, Router-ID %pI4", + IF_NAME(oi), + &ospfh->router_id); + return 0; + } + return 1; + case OSPF_AUTH_SIMPLE: /* RFC2328 D.5.2 */ + if (iface_auth_type != OSPF_AUTH_SIMPLE) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_PACKET, + "interface %s: auth-type mismatch, local %s, rcvd Simple, Router-ID %pI4", + IF_NAME(oi), + lookup_msg(ospf_auth_type_str, + iface_auth_type, NULL), + &ospfh->router_id); + return 0; + } + if (memcmp(OSPF_IF_PARAM(oi, auth_simple), ospfh->u.auth_data, + OSPF_AUTH_SIMPLE_SIZE)) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_PACKET, + "interface %s: Simple auth failed, Router-ID %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + } + if (!ospf_check_sum(ospfh)) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_PACKET, + "interface %s: Simple auth OK, checksum error, Router-ID %pI4", + IF_NAME(oi), + &ospfh->router_id); + return 0; + } + return 1; + case OSPF_AUTH_CRYPTOGRAPHIC: /* RFC2328 D.5.3 */ + if (iface_auth_type != OSPF_AUTH_CRYPTOGRAPHIC) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_PACKET, + "interface %s: auth-type mismatch, local %s, rcvd Cryptographic, Router-ID %pI4", + IF_NAME(oi), + lookup_msg(ospf_auth_type_str, + iface_auth_type, NULL), + &ospfh->router_id); + return 0; + } + if (ospfh->checksum) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_PACKET, + "interface %s: OSPF header checksum is not 0, Router-ID %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + } + /* If `authentication message-digest` key is not set, we try keychain crypto */ + if (OSPF_IF_PARAM(oi, keychain_name) || !list_isempty(OSPF_IF_PARAM(oi, auth_crypt))) + return ospf_auth_check_digest(oi, iph, ospfh); + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_AUTH, + "interface %s: MD5 auth failed, Router-ID %pI4", + IF_NAME(oi), &ospfh->router_id); + return 0; + default: + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) + flog_warn( + EC_OSPF_PACKET, + "interface %s: invalid packet auth-type (%02x), Router-ID %pI4", + IF_NAME(oi), pkt_auth_type, &ospfh->router_id); + return 0; + } +} + +/* OSPF authentication checking function */ +int ospf_auth_type(struct ospf_interface *oi) +{ + int auth_type; + + if (OSPF_IF_PARAM(oi, auth_type) == OSPF_AUTH_NOTSET) + auth_type = oi->area->auth_type; + else + auth_type = OSPF_IF_PARAM(oi, auth_type); + + /* Handle case where MD5 key list, or a key-chain, is not configured aka Cisco */ + if (auth_type == OSPF_AUTH_CRYPTOGRAPHIC + && (list_isempty(OSPF_IF_PARAM(oi, auth_crypt)) + && OSPF_IF_PARAM(oi, keychain_name) == NULL)) + return OSPF_AUTH_NULL; + + return auth_type; +} + +/* Make Authentication Data. */ +int ospf_auth_make_data(struct ospf_interface *oi, struct ospf_header *ospfh) +{ + struct crypt_key *ck; + + switch (ospf_auth_type(oi)) { + case OSPF_AUTH_NULL: + /* memset (ospfh->u.auth_data, 0, sizeof(ospfh->u.auth_data)); + */ + break; + case OSPF_AUTH_SIMPLE: + memcpy(ospfh->u.auth_data, OSPF_IF_PARAM(oi, auth_simple), + OSPF_AUTH_SIMPLE_SIZE); + break; + case OSPF_AUTH_CRYPTOGRAPHIC: + if (OSPF_IF_PARAM(oi, keychain_name)) { + oi->keychain = keychain_lookup(OSPF_IF_PARAM(oi, keychain_name)); + if (oi->keychain) + oi->key = key_lookup_for_send(oi->keychain); + if (oi->key) { + ospfh->u.crypt.zero = 0; + ospfh->u.crypt.key_id = oi->key->index; + ospfh->u.crypt.auth_data_len = keychain_get_hash_len(oi->key->hash_algo); + } else { + /* If key is not set, then set 0. */ + ospfh->u.crypt.zero = 0; + ospfh->u.crypt.key_id = 0; + ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE; + } + } else { + /* If key is not set, then set 0. */ + if (list_isempty(OSPF_IF_PARAM(oi, auth_crypt))) { + ospfh->u.crypt.zero = 0; + ospfh->u.crypt.key_id = 0; + ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE; + } else { + ck = listgetdata( + listtail(OSPF_IF_PARAM(oi, auth_crypt))); + ospfh->u.crypt.zero = 0; + ospfh->u.crypt.key_id = ck->key_id; + ospfh->u.crypt.auth_data_len = OSPF_AUTH_MD5_SIZE; + } + } + /* note: the seq is done in ospf_auth_make() */ + break; + default: + /* memset (ospfh->u.auth_data, 0, sizeof(ospfh->u.auth_data)); + */ + break; + } + + return 0; +} diff --git a/ospfd/ospf_auth.h b/ospfd/ospf_auth.h new file mode 100644 index 0000000..6f6d3db --- /dev/null +++ b/ospfd/ospf_auth.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 Amnesh Inc. + * Mahdi Varasteh + */ + +#ifndef _ZEBRA_OSPF_AUTH_H +#define _ZEBRA_OSPF_AUTH_H + +#include +#include + +int ospf_auth_check(struct ospf_interface *oi, struct ip *iph, struct ospf_header *ospfh); +int ospf_auth_check_digest(struct ospf_interface *oi, struct ip *iph, struct ospf_header *ospfh); +int ospf_auth_make(struct ospf_interface *oi, struct ospf_packet *op); +int ospf_auth_make_digest(struct ospf_interface *oi, struct ospf_packet *op); +int ospf_auth_type(struct ospf_interface *oi); +int ospf_auth_make_data(struct ospf_interface *oi, struct ospf_header *ospfh); + +#endif /* _ZEBRA_OSPF_AUTH_H */ diff --git a/ospfd/ospf_bfd.c b/ospfd/ospf_bfd.c new file mode 100644 index 0000000..7d4c7c0 --- /dev/null +++ b/ospfd/ospf_bfd.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * ospf_bfd.c: OSPF BFD handling routines + * + * @copyright Copyright (C) 2015 Cumulus Networks, Inc. + */ + +#include + +#include "command.h" +#include "json.h" +#include "linklist.h" +#include "memory.h" +#include "prefix.h" +#include "frrevent.h" +#include "buffer.h" +#include "stream.h" +#include "zclient.h" +#include "vty.h" +#include "table.h" +#include "bfd.h" +#include "ospfd.h" +#include "ospf_asbr.h" +#include "ospf_lsa.h" +#include "ospf_lsdb.h" +#include "ospf_neighbor.h" +#include "ospf_interface.h" +#include "ospf_nsm.h" +#include "ospf_bfd.h" +#include "ospf_dump.h" +#include "ospf_vty.h" + +DEFINE_MTYPE_STATIC(OSPFD, BFD_CONFIG, "BFD configuration data"); + +/* + * ospf_bfd_trigger_event - Neighbor is registered/deregistered with BFD when + * neighbor state is changed to/from 2way. + */ +void ospf_bfd_trigger_event(struct ospf_neighbor *nbr, int old_state, int state) +{ + if ((old_state < NSM_TwoWay) && (state >= NSM_TwoWay)) + bfd_sess_install(nbr->bfd_session); + else if ((old_state >= NSM_TwoWay) && (state < NSM_TwoWay)) + bfd_sess_uninstall(nbr->bfd_session); +} + +static void ospf_bfd_session_change(struct bfd_session_params *bsp, + const struct bfd_session_status *bss, + void *arg) +{ + struct ospf_neighbor *nbr = arg; + + /* BFD peer went down. */ + if (bss->state == BFD_STATUS_DOWN + && bss->previous_state == BFD_STATUS_UP) { + if (IS_DEBUG_OSPF(bfd, BFD_LIB)) + zlog_debug("%s: NSM[%s:%pI4]: BFD Down", __func__, + IF_NAME(nbr->oi), &nbr->address.u.prefix4); + + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_InactivityTimer); + } + + /* BFD peer went up. */ + if (bss->state == BSS_UP && bss->previous_state == BSS_DOWN) + if (IS_DEBUG_OSPF(bfd, BFD_LIB)) + zlog_debug("%s: NSM[%s:%pI4]: BFD Up", __func__, + IF_NAME(nbr->oi), &nbr->address.u.prefix4); +} + +void ospf_neighbor_bfd_apply(struct ospf_neighbor *nbr) +{ + struct ospf_interface *oi = nbr->oi; + struct ospf_if_params *oip = IF_DEF_PARAMS(oi->ifp); + + /* BFD configuration was removed. */ + if (oip->bfd_config == NULL) { + bfd_sess_free(&nbr->bfd_session); + return; + } + + /* New BFD session. */ + if (nbr->bfd_session == NULL) { + nbr->bfd_session = bfd_sess_new(ospf_bfd_session_change, nbr); + bfd_sess_set_ipv4_addrs(nbr->bfd_session, NULL, &nbr->src); + bfd_sess_set_interface(nbr->bfd_session, oi->ifp->name); + bfd_sess_set_vrf(nbr->bfd_session, oi->ospf->vrf_id); + } + + /* Set new configuration. */ + bfd_sess_set_timers(nbr->bfd_session, + oip->bfd_config->detection_multiplier, + oip->bfd_config->min_rx, oip->bfd_config->min_tx); + bfd_sess_set_profile(nbr->bfd_session, oip->bfd_config->profile); + + /* Don't start sessions on down OSPF sessions. */ + if (nbr->state < NSM_TwoWay) + return; + + bfd_sess_install(nbr->bfd_session); +} + +static void ospf_interface_bfd_apply(struct interface *ifp) +{ + struct ospf_interface *oi; + struct route_table *nbrs; + struct ospf_neighbor *nbr; + struct route_node *irn; + struct route_node *nrn; + + /* Iterate over all interfaces and set neighbors BFD session. */ + for (irn = route_top(IF_OIFS(ifp)); irn; irn = route_next(irn)) { + if ((oi = irn->info) == NULL) + continue; + if ((nbrs = oi->nbrs) == NULL) + continue; + for (nrn = route_top(nbrs); nrn; nrn = route_next(nrn)) { + if ((nbr = nrn->info) == NULL || nbr == oi->nbr_self) + continue; + + ospf_neighbor_bfd_apply(nbr); + } + } +} + +static void ospf_interface_enable_bfd(struct interface *ifp) +{ + struct ospf_if_params *oip = IF_DEF_PARAMS(ifp); + + if (oip->bfd_config) + return; + + /* Allocate memory for configurations and set defaults. */ + oip->bfd_config = XCALLOC(MTYPE_BFD_CONFIG, sizeof(*oip->bfd_config)); + oip->bfd_config->detection_multiplier = BFD_DEF_DETECT_MULT; + oip->bfd_config->min_rx = BFD_DEF_MIN_RX; + oip->bfd_config->min_tx = BFD_DEF_MIN_TX; +} + +void ospf_interface_disable_bfd(struct interface *ifp, + struct ospf_if_params *oip) +{ + XFREE(MTYPE_BFD_CONFIG, oip->bfd_config); + ospf_interface_bfd_apply(ifp); +} + +/* + * ospf_bfd_write_config - Write the interface BFD configuration. + */ +void ospf_bfd_write_config(struct vty *vty, const struct ospf_if_params *params + __attribute__((unused))) +{ +#if HAVE_BFDD == 0 + if (params->bfd_config->detection_multiplier != BFD_DEF_DETECT_MULT + || params->bfd_config->min_rx != BFD_DEF_MIN_RX + || params->bfd_config->min_tx != BFD_DEF_MIN_TX) + vty_out(vty, " ip ospf bfd %d %d %d\n", + params->bfd_config->detection_multiplier, + params->bfd_config->min_rx, params->bfd_config->min_tx); + else +#endif /* ! HAVE_BFDD */ + vty_out(vty, " ip ospf bfd\n"); + + if (params->bfd_config->profile[0]) + vty_out(vty, " ip ospf bfd profile %s\n", + params->bfd_config->profile); +} + +void ospf_interface_bfd_show(struct vty *vty, const struct interface *ifp, + struct json_object *json) +{ + struct ospf_if_params *params = IF_DEF_PARAMS(ifp); + struct bfd_configuration *bfd_config = params->bfd_config; + struct json_object *json_bfd; + + if (bfd_config == NULL) + return; + + if (json) { + json_bfd = json_object_new_object(); + json_object_int_add(json_bfd, "detectionMultiplier", + bfd_config->detection_multiplier); + json_object_int_add(json_bfd, "rxMinInterval", + bfd_config->min_rx); + json_object_int_add(json_bfd, "txMinInterval", + bfd_config->min_tx); + json_object_object_add(json, "peerBfdInfo", json_bfd); + } else + vty_out(vty, + " BFD: Detect Multiplier: %d, Min Rx interval: %d, Min Tx interval: %d\n", + bfd_config->detection_multiplier, bfd_config->min_rx, + bfd_config->min_tx); +} + +DEFUN (ip_ospf_bfd, + ip_ospf_bfd_cmd, + "ip ospf bfd", + "IP Information\n" + "OSPF interface commands\n" + "Enables BFD support\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + ospf_interface_enable_bfd(ifp); + ospf_interface_bfd_apply(ifp); + return CMD_SUCCESS; +} + +#if HAVE_BFDD > 0 +DEFUN_HIDDEN( +#else +DEFUN( +#endif /* HAVE_BFDD */ + ip_ospf_bfd_param, + ip_ospf_bfd_param_cmd, + "ip ospf bfd (2-255) (50-60000) (50-60000)", + "IP Information\n" + "OSPF interface commands\n" + "Enables BFD support\n" + "Detect Multiplier\n" + "Required min receive interval\n" + "Desired min transmit interval\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + int idx_number = 3; + int idx_number_2 = 4; + int idx_number_3 = 5; + + ospf_interface_enable_bfd(ifp); + + params = IF_DEF_PARAMS(ifp); + params->bfd_config->detection_multiplier = + strtol(argv[idx_number]->arg, NULL, 10); + params->bfd_config->min_rx = strtol(argv[idx_number_2]->arg, NULL, 10); + params->bfd_config->min_tx = strtol(argv[idx_number_3]->arg, NULL, 10); + + ospf_interface_bfd_apply(ifp); + + return CMD_SUCCESS; +} + +DEFUN (ip_ospf_bfd_prof, + ip_ospf_bfd_prof_cmd, + "ip ospf bfd profile BFDPROF", + "IP Information\n" + "OSPF interface commands\n" + "Enables BFD support\n" + BFD_PROFILE_STR + BFD_PROFILE_NAME_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + int idx_prof = 4; + + params = IF_DEF_PARAMS(ifp); + if (!params->bfd_config) { + vty_out(vty, "ip ospf bfd has not been set\n"); + return CMD_WARNING; + } + + strlcpy(params->bfd_config->profile, argv[idx_prof]->arg, + sizeof(params->bfd_config->profile)); + ospf_interface_bfd_apply(ifp); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_ospf_bfd_prof, + no_ip_ospf_bfd_prof_cmd, + "no ip ospf bfd profile [BFDPROF]", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Enables BFD support\n" + BFD_PROFILE_STR + BFD_PROFILE_NAME_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + + params = IF_DEF_PARAMS(ifp); + if (!params->bfd_config) + return CMD_SUCCESS; + + params->bfd_config->profile[0] = 0; + ospf_interface_bfd_apply(ifp); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_ospf_bfd, + no_ip_ospf_bfd_cmd, +#if HAVE_BFDD > 0 + "no ip ospf bfd", +#else + "no ip ospf bfd [(2-255) (50-60000) (50-60000)]", +#endif /* HAVE_BFDD */ + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Disables BFD support\n" +#if HAVE_BFDD == 0 + "Detect Multiplier\n" + "Required min receive interval\n" + "Desired min transmit interval\n" +#endif /* !HAVE_BFDD */ +) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + ospf_interface_disable_bfd(ifp, IF_DEF_PARAMS(ifp)); + return CMD_SUCCESS; +} + +void ospf_bfd_init(struct event_loop *tm) +{ + bfd_protocol_integration_init(zclient, tm); + + /* Install BFD command */ + install_element(INTERFACE_NODE, &ip_ospf_bfd_cmd); + install_element(INTERFACE_NODE, &ip_ospf_bfd_param_cmd); + install_element(INTERFACE_NODE, &ip_ospf_bfd_prof_cmd); + install_element(INTERFACE_NODE, &no_ip_ospf_bfd_prof_cmd); + install_element(INTERFACE_NODE, &no_ip_ospf_bfd_cmd); +} diff --git a/ospfd/ospf_bfd.h b/ospfd/ospf_bfd.h new file mode 100644 index 0000000..d454f9c --- /dev/null +++ b/ospfd/ospf_bfd.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * ospf_bfd.h: OSPF BFD definitions and structures + * + * @copyright Copyright (C) 2015 Cumulus Networks, Inc. + */ + +#ifndef _ZEBRA_OSPF_BFD_H +#define _ZEBRA_OSPF_BFD_H + +#include "ospfd/ospf_interface.h" +#include "json.h" + +extern void ospf_bfd_init(struct event_loop *tm); + +extern void ospf_bfd_write_config(struct vty *vty, + const struct ospf_if_params *params); + +extern void ospf_bfd_trigger_event(struct ospf_neighbor *nbr, int old_state, + int state); + +/** + * Legacy information: it is the peers who actually have this information + * and the protocol should not need to know about timers. + */ +extern void ospf_interface_bfd_show(struct vty *vty, + const struct interface *ifp, + struct json_object *json); + +/** + * Disables interface BFD configuration and remove settings from all peers. + */ +extern void ospf_interface_disable_bfd(struct interface *ifp, + struct ospf_if_params *oip); + +/** + * Create/update BFD session for this OSPF neighbor. + */ +extern void ospf_neighbor_bfd_apply(struct ospf_neighbor *nbr); + +#endif /* _ZEBRA_OSPF_BFD_H */ diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c new file mode 100644 index 0000000..dbe6dd9 --- /dev/null +++ b/ospfd/ospf_dump.c @@ -0,0 +1,2072 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPFd dump routine. + * Copyright (C) 1999, 2000 Toshiaki Takada + */ + +#include + +#include "lib/bfd.h" +#include "monotime.h" +#include "linklist.h" +#include "frrevent.h" +#include "prefix.h" +#include "command.h" +#include "stream.h" +#include "log.h" +#include "sockopt.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_network.h" +#include "ospfd/ospf_dump_clippy.c" + +/* Configuration debug option variables. */ +unsigned long conf_debug_ospf_packet[5] = {0, 0, 0, 0, 0}; +unsigned long conf_debug_ospf_event = 0; +unsigned long conf_debug_ospf_ism = 0; +unsigned long conf_debug_ospf_nsm = 0; +unsigned long conf_debug_ospf_lsa = 0; +unsigned long conf_debug_ospf_zebra = 0; +unsigned long conf_debug_ospf_nssa = 0; +unsigned long conf_debug_ospf_te; +unsigned long conf_debug_ospf_ext = 0; +unsigned long conf_debug_ospf_sr; +unsigned long conf_debug_ospf_ti_lfa; +unsigned long conf_debug_ospf_defaultinfo; +unsigned long conf_debug_ospf_ldp_sync; +unsigned long conf_debug_ospf_gr; +unsigned long conf_debug_ospf_bfd; +unsigned long conf_debug_ospf_client_api; + +/* Enable debug option variables -- valid only session. */ +unsigned long term_debug_ospf_packet[5] = {0, 0, 0, 0, 0}; +unsigned long term_debug_ospf_event; +unsigned long term_debug_ospf_ism = 0; +unsigned long term_debug_ospf_nsm = 0; +unsigned long term_debug_ospf_lsa = 0; +unsigned long term_debug_ospf_zebra = 0; +unsigned long term_debug_ospf_nssa = 0; +unsigned long term_debug_ospf_te; +unsigned long term_debug_ospf_ext = 0; +unsigned long term_debug_ospf_sr; +unsigned long term_debug_ospf_ti_lfa; +unsigned long term_debug_ospf_defaultinfo; +unsigned long term_debug_ospf_ldp_sync; +unsigned long term_debug_ospf_gr; +unsigned long term_debug_ospf_bfd; +unsigned long term_debug_ospf_client_api; + +const char *ospf_redist_string(unsigned int route_type) +{ + return (route_type == ZEBRA_ROUTE_MAX) ? "Default" + : zebra_route_string(route_type); +} + +#define OSPF_AREA_STRING_MAXLEN 16 +const char *ospf_area_name_string(struct ospf_area *area) +{ + static char buf[OSPF_AREA_STRING_MAXLEN] = ""; + uint32_t area_id; + + if (!area) + return "-"; + + area_id = ntohl(area->area_id.s_addr); + snprintf(buf, sizeof(buf), "%d.%d.%d.%d", (area_id >> 24) & 0xff, + (area_id >> 16) & 0xff, (area_id >> 8) & 0xff, area_id & 0xff); + return buf; +} + +#define OSPF_AREA_DESC_STRING_MAXLEN 23 +const char *ospf_area_desc_string(struct ospf_area *area) +{ + static char buf[OSPF_AREA_DESC_STRING_MAXLEN] = ""; + uint8_t type; + + if (!area) + return "(incomplete)"; + + type = area->external_routing; + switch (type) { + case OSPF_AREA_NSSA: + snprintf(buf, sizeof(buf), "%s [NSSA]", + ospf_area_name_string(area)); + break; + case OSPF_AREA_STUB: + snprintf(buf, sizeof(buf), "%s [Stub]", + ospf_area_name_string(area)); + break; + default: + return ospf_area_name_string(area); + } + + return buf; +} + +#define OSPF_IF_STRING_MAXLEN 40 + +/* Display both nbr and ism state of the ospf neighbor.*/ +const char *ospf_if_name_string(struct ospf_interface *oi) +{ + static char buf[OSPF_IF_STRING_MAXLEN] = ""; + uint32_t ifaddr; + + if (!oi || !oi->address) + return "inactive"; + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + return oi->ifp->name; + + ifaddr = ntohl(oi->address->u.prefix4.s_addr); + snprintf(buf, sizeof(buf), "%s:%d.%d.%d.%d", oi->ifp->name, + (ifaddr >> 24) & 0xff, (ifaddr >> 16) & 0xff, + (ifaddr >> 8) & 0xff, ifaddr & 0xff); + return buf; +} + +int ospf_nbr_ism_state(struct ospf_neighbor *nbr) +{ + int state; + struct ospf_interface *oi = nbr->oi; + + if (IPV4_ADDR_SAME(&DR(oi), &nbr->address.u.prefix4)) + state = ISM_DR; + else if (IPV4_ADDR_SAME(&BDR(oi), &nbr->address.u.prefix4)) + state = ISM_Backup; + else + state = ISM_DROther; + + return state; +} + +void ospf_nbr_ism_state_message(struct ospf_neighbor *nbr, char *buf, + size_t size) +{ + int state; + struct ospf_interface *oi = nbr->oi; + + if (!oi) + return; + + /* network type is point-to-point */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT) { + snprintf(buf, size, "%s/-", + lookup_msg(ospf_nsm_state_msg, nbr->state, NULL)); + return; + } + + state = ospf_nbr_ism_state(nbr); + + snprintf(buf, size, "%s/%s", + lookup_msg(ospf_nsm_state_msg, nbr->state, NULL), + lookup_msg(ospf_ism_state_msg, state, NULL)); +} + +const char *ospf_timeval_dump(struct timeval *t, char *buf, size_t size) +{ +/* Making formatted timer strings. */ +#define MINUTE_IN_SECONDS 60 +#define HOUR_IN_SECONDS (60*MINUTE_IN_SECONDS) + + unsigned long w, d, h, m, ms, us; + + if (!t) + return "inactive"; + + w = d = h = m = ms = 0; + memset(buf, 0, size); + + us = t->tv_usec; + if (us >= 1000) { + ms = us / 1000; + us %= 1000; + (void)us; /* unused */ + } + + if (ms >= 1000) { + t->tv_sec += ms / 1000; + ms %= 1000; + } + + if (t->tv_sec > ONE_WEEK_SECOND) { + w = t->tv_sec / ONE_WEEK_SECOND; + t->tv_sec -= w * ONE_WEEK_SECOND; + } + + if (t->tv_sec > ONE_DAY_SECOND) { + d = t->tv_sec / ONE_DAY_SECOND; + t->tv_sec -= d * ONE_DAY_SECOND; + } + + if (t->tv_sec >= HOUR_IN_SECONDS) { + h = t->tv_sec / HOUR_IN_SECONDS; + t->tv_sec -= h * HOUR_IN_SECONDS; + } + + if (t->tv_sec >= MINUTE_IN_SECONDS) { + m = t->tv_sec / MINUTE_IN_SECONDS; + t->tv_sec -= m * MINUTE_IN_SECONDS; + } + + if (w > 99) + snprintf(buf, size, "%luw%1lud", w, d); + else if (w) + snprintf(buf, size, "%luw%1lud%02luh", w, d, h); + else if (d) + snprintf(buf, size, "%1lud%02luh%02lum", d, h, m); + else if (h) + snprintf(buf, size, "%luh%02lum%02lds", h, m, (long)t->tv_sec); + else if (m) + snprintf(buf, size, "%lum%02lds", m, (long)t->tv_sec); + else if (t->tv_sec > 0 || ms > 0) + snprintf(buf, size, "%ld.%03lus", (long)t->tv_sec, ms); + else + snprintf(buf, size, "%ld usecs", (long)t->tv_usec); + + return buf; +} + +const char *ospf_timer_dump(struct event *t, char *buf, size_t size) +{ + struct timeval result; + if (!t) + return "inactive"; + + monotime_until(&t->u.sands, &result); + return ospf_timeval_dump(&result, buf, size); +} + +static void ospf_packet_hello_dump(struct stream *s, uint16_t length) +{ + struct ospf_hello *hello; + int i, len; + + hello = (struct ospf_hello *)stream_pnt(s); + + zlog_debug("Hello"); + zlog_debug(" NetworkMask %pI4", &hello->network_mask); + zlog_debug(" HelloInterval %d", ntohs(hello->hello_interval)); + zlog_debug(" Options %d (%s)", hello->options, + ospf_options_dump(hello->options)); + zlog_debug(" RtrPriority %d", hello->priority); + zlog_debug(" RtrDeadInterval %ld", + (unsigned long)ntohl(hello->dead_interval)); + zlog_debug(" DRouter %pI4", &hello->d_router); + zlog_debug(" BDRouter %pI4", &hello->bd_router); + + len = length - OSPF_HEADER_SIZE - OSPF_HELLO_MIN_SIZE; + zlog_debug(" # Neighbors %d", len / 4); + for (i = 0; len > 0; i++, len -= sizeof(struct in_addr)) + zlog_debug(" Neighbor %pI4", &hello->neighbors[i]); +} + +static char *ospf_dd_flags_dump(uint8_t flags, char *buf, size_t size) +{ + snprintf(buf, size, "%s|%s|%s", (flags & OSPF_DD_FLAG_I) ? "I" : "-", + (flags & OSPF_DD_FLAG_M) ? "M" : "-", + (flags & OSPF_DD_FLAG_MS) ? "MS" : "-"); + + return buf; +} + +static char *ospf_router_lsa_flags_dump(uint8_t flags, char *buf, size_t size) +{ + snprintf(buf, size, "%s|%s|%s", + (flags & ROUTER_LSA_VIRTUAL) ? "V" : "-", + (flags & ROUTER_LSA_EXTERNAL) ? "E" : "-", + (flags & ROUTER_LSA_BORDER) ? "B" : "-"); + + return buf; +} + +static void ospf_router_lsa_dump(struct stream *s, uint16_t length) +{ + char buf[BUFSIZ]; + struct router_lsa *rl; + struct router_link *rlnk; + int i, len, sum; + + rl = (struct router_lsa *)stream_pnt(s); + + zlog_debug(" Router-LSA"); + zlog_debug(" flags %s", + ospf_router_lsa_flags_dump(rl->flags, buf, BUFSIZ)); + zlog_debug(" # links %d", ntohs(rl->links)); + + len = length - OSPF_LSA_HEADER_SIZE - 4; + rlnk = &rl->link[0]; + sum = 0; + for (i = 0; sum < len && rlnk; sum += 12, rlnk = &rl->link[++i]) { + zlog_debug(" Link ID %pI4", &rlnk->link_id); + zlog_debug(" Link Data %pI4", &rlnk->link_data); + zlog_debug(" Type %d", (uint8_t)rlnk->type); + zlog_debug(" TOS %d", (uint8_t)rlnk->tos); + zlog_debug(" metric %d", ntohs(rlnk->metric)); + } +} + +static void ospf_network_lsa_dump(struct stream *s, uint16_t length) +{ + struct network_lsa *nl; + int i, cnt; + + zlog_debug(" Network-LSA"); + + nl = (struct network_lsa *)stream_pnt(s); + cnt = (length - (OSPF_LSA_HEADER_SIZE + 4)) / 4; + + /* + zlog_debug ("LSA total size %d", ntohs (nl->header.length)); + zlog_debug ("Network-LSA size %d", + ntohs (nl->header.length) - OSPF_LSA_HEADER_SIZE); + */ + zlog_debug(" Network Mask %pI4", &nl->mask); + zlog_debug(" # Attached Routers %d", cnt); + for (i = 0; i < cnt; i++) + zlog_debug(" Attached Router %pI4", + &nl->routers[i]); +} + +static void ospf_summary_lsa_dump(struct stream *s, uint16_t length) +{ + struct summary_lsa *sl; + + sl = (struct summary_lsa *)stream_pnt(s); + + zlog_debug(" Summary-LSA"); + zlog_debug(" Network Mask %pI4", &sl->mask); + zlog_debug(" TOS=%d metric %d", sl->tos, GET_METRIC(sl->metric)); +} + +static void ospf_as_external_lsa_dump(struct stream *s, uint16_t length) +{ + struct as_external_lsa *al; + struct as_route *asr; + int size, sum; + int i; + + al = (struct as_external_lsa *)stream_pnt(s); + zlog_debug(" %s", ospf_lsa_type_msg[al->header.type].str); + zlog_debug(" Network Mask %pI4", &al->mask); + + size = length - OSPF_LSA_HEADER_SIZE - 4; + asr = &al->e[0]; + sum = 0; + for (i = 0; sum < size && asr; sum += 12, asr = &al->e[++i]) { + zlog_debug(" bit %s TOS=%d metric %d", + IS_EXTERNAL_METRIC(asr->tos) ? "E" : "-", + asr->tos & 0x7f, GET_METRIC(asr->metric)); + zlog_debug(" Forwarding address %pI4", &asr->fwd_addr); + zlog_debug(" External Route Tag %" ROUTE_TAG_PRI, + ntohl(asr->route_tag)); + } +} + +static void ospf_lsa_header_list_dump(struct stream *s, uint16_t length) +{ + struct lsa_header *lsa; + int len; + + zlog_debug(" # LSA Headers %d", length / OSPF_LSA_HEADER_SIZE); + + /* LSA Headers. */ + len = length; + while (len > 0) { + lsa = (struct lsa_header *)stream_pnt(s); + ospf_lsa_header_dump(lsa); + + stream_forward_getp(s, OSPF_LSA_HEADER_SIZE); + len -= OSPF_LSA_HEADER_SIZE; + } +} + +static void ospf_packet_db_desc_dump(struct stream *s, uint16_t length) +{ + struct ospf_db_desc *dd; + char dd_flags[8]; + + uint32_t gp; + + gp = stream_get_getp(s); + dd = (struct ospf_db_desc *)stream_pnt(s); + + zlog_debug("Database Description"); + zlog_debug(" Interface MTU %d", ntohs(dd->mtu)); + zlog_debug(" Options %d (%s)", dd->options, + ospf_options_dump(dd->options)); + zlog_debug(" Flags %d (%s)", dd->flags, + ospf_dd_flags_dump(dd->flags, dd_flags, sizeof(dd_flags))); + zlog_debug(" Sequence Number 0x%08lx", + (unsigned long)ntohl(dd->dd_seqnum)); + + length -= OSPF_HEADER_SIZE + OSPF_DB_DESC_MIN_SIZE; + + stream_forward_getp(s, OSPF_DB_DESC_MIN_SIZE); + + ospf_lsa_header_list_dump(s, length); + + stream_set_getp(s, gp); +} + +static void ospf_packet_ls_req_dump(struct stream *s, uint16_t length) +{ + uint32_t sp; + uint32_t ls_type; + struct in_addr ls_id; + struct in_addr adv_router; + int sum; + + sp = stream_get_getp(s); + + length -= OSPF_HEADER_SIZE; + + zlog_debug("Link State Request"); + zlog_debug(" # Requests %d", length / 12); + + sum = 0; + for (; sum < length; sum += 12) { + ls_type = stream_getl(s); + ls_id.s_addr = stream_get_ipv4(s); + adv_router.s_addr = stream_get_ipv4(s); + + zlog_debug(" LS type %d", ls_type); + zlog_debug(" Link State ID %pI4", &ls_id); + zlog_debug(" Advertising Router %pI4", &adv_router); + } + + stream_set_getp(s, sp); +} + +static void ospf_packet_ls_upd_dump(struct stream *s, uint16_t length) +{ + uint32_t sp; + struct lsa_header *lsa; + int lsa_len, len; + uint32_t count; + + len = length - OSPF_HEADER_SIZE; + + sp = stream_get_getp(s); + + count = stream_getl(s); + len -= 4; + + zlog_debug("Link State Update"); + zlog_debug(" # LSAs %d", count); + + while (len > 0 && count > 0) { + if ((uint16_t)len < OSPF_LSA_HEADER_SIZE || len % 4 != 0) { + zlog_debug(" Remaining %d bytes; Incorrect length.", + len); + break; + } + + lsa = (struct lsa_header *)stream_pnt(s); + lsa_len = ntohs(lsa->length); + ospf_lsa_header_dump(lsa); + + /* Check that LSA length is valid */ + if (lsa_len > len || lsa_len % 4 != 0) { + zlog_debug(" LSA length %d is incorrect!", lsa_len); + break; + } + switch (lsa->type) { + case OSPF_ROUTER_LSA: + ospf_router_lsa_dump(s, lsa_len); + break; + case OSPF_NETWORK_LSA: + ospf_network_lsa_dump(s, lsa_len); + break; + case OSPF_SUMMARY_LSA: + case OSPF_ASBR_SUMMARY_LSA: + ospf_summary_lsa_dump(s, lsa_len); + break; + case OSPF_AS_EXTERNAL_LSA: + ospf_as_external_lsa_dump(s, lsa_len); + break; + case OSPF_AS_NSSA_LSA: + ospf_as_external_lsa_dump(s, lsa_len); + break; + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + ospf_opaque_lsa_dump(s, lsa_len); + break; + default: + break; + } + + stream_forward_getp(s, lsa_len); + len -= lsa_len; + count--; + } + + stream_set_getp(s, sp); +} + +static void ospf_packet_ls_ack_dump(struct stream *s, uint16_t length) +{ + uint32_t sp; + + length -= OSPF_HEADER_SIZE; + sp = stream_get_getp(s); + + zlog_debug("Link State Acknowledgment"); + ospf_lsa_header_list_dump(s, length); + + stream_set_getp(s, sp); +} + +static void ospf_header_dump(struct ospf_header *ospfh) +{ + char buf[9]; + uint16_t auth_type = ntohs(ospfh->auth_type); + + zlog_debug("Header"); + zlog_debug(" Version %d", ospfh->version); + zlog_debug(" Type %d (%s)", ospfh->type, + lookup_msg(ospf_packet_type_str, ospfh->type, NULL)); + zlog_debug(" Packet Len %d", ntohs(ospfh->length)); + zlog_debug(" Router ID %pI4", &ospfh->router_id); + zlog_debug(" Area ID %pI4", &ospfh->area_id); + zlog_debug(" Checksum 0x%x", ntohs(ospfh->checksum)); + zlog_debug(" AuType %s", + lookup_msg(ospf_auth_type_str, auth_type, NULL)); + + switch (auth_type) { + case OSPF_AUTH_NULL: + break; + case OSPF_AUTH_SIMPLE: + strlcpy(buf, (char *)ospfh->u.auth_data, sizeof(buf)); + zlog_debug(" Simple Password %s", buf); + break; + case OSPF_AUTH_CRYPTOGRAPHIC: + zlog_debug(" Cryptographic Authentication"); + zlog_debug(" Key ID %d", ospfh->u.crypt.key_id); + zlog_debug(" Auth Data Len %d", ospfh->u.crypt.auth_data_len); + zlog_debug(" Sequence number %ld", + (unsigned long)ntohl(ospfh->u.crypt.crypt_seqnum)); + break; + default: + zlog_debug("* This is not supported authentication type"); + break; + } +} + +void ospf_packet_dump(struct stream *s) +{ + struct ospf_header *ospfh; + unsigned long gp; + + /* Preserve pointer. */ + gp = stream_get_getp(s); + + /* OSPF Header dump. */ + ospfh = (struct ospf_header *)stream_pnt(s); + + /* Until detail flag is set, return. */ + if (!(term_debug_ospf_packet[ospfh->type - 1] & OSPF_DEBUG_DETAIL)) + return; + + /* Show OSPF header detail. */ + ospf_header_dump(ospfh); + stream_forward_getp(s, OSPF_HEADER_SIZE); + + switch (ospfh->type) { + case OSPF_MSG_HELLO: + ospf_packet_hello_dump(s, ntohs(ospfh->length)); + break; + case OSPF_MSG_DB_DESC: + ospf_packet_db_desc_dump(s, ntohs(ospfh->length)); + break; + case OSPF_MSG_LS_REQ: + ospf_packet_ls_req_dump(s, ntohs(ospfh->length)); + break; + case OSPF_MSG_LS_UPD: + ospf_packet_ls_upd_dump(s, ntohs(ospfh->length)); + break; + case OSPF_MSG_LS_ACK: + ospf_packet_ls_ack_dump(s, ntohs(ospfh->length)); + break; + default: + break; + } + + stream_set_getp(s, gp); +} + +DEFPY (debug_ospf_packet, + debug_ospf_packet_cmd, + "[no$no] debug ospf [(1-65535)$inst] packet $packet []", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF packets\n" + "OSPF Hello\n" + "OSPF Database Description\n" + "OSPF Link State Request\n" + "OSPF Link State Update\n" + "OSPF Link State Acknowledgment\n" + "OSPF all packets\n" + "Packet sent\n" + "Detail Information\n" + "Packet received\n" + "Detail Information\n" + "Detail Information\n") +{ + int type = 0; + int flag = 0; + int i; + + if (inst && inst != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + /* Check packet type. */ + if (strmatch(packet, "hello")) + type = OSPF_DEBUG_HELLO; + else if (strmatch(packet, "dd")) + type = OSPF_DEBUG_DB_DESC; + else if (strmatch(packet, "ls-request")) + type = OSPF_DEBUG_LS_REQ; + else if (strmatch(packet, "ls-update")) + type = OSPF_DEBUG_LS_UPD; + else if (strmatch(packet, "ls-ack")) + type = OSPF_DEBUG_LS_ACK; + else if (strmatch(packet, "all")) + type = OSPF_DEBUG_ALL; + + /* Cases: + * (none) = send + recv + * detail = send + recv + detail + * recv = recv + * send = send + * recv detail = recv + detail + * send detail = send + detail + */ + if (!send && !recv) { + flag |= OSPF_DEBUG_SEND; + flag |= OSPF_DEBUG_RECV; + } + + flag |= (send) ? OSPF_DEBUG_SEND : 0; + flag |= (recv) ? OSPF_DEBUG_RECV : 0; + flag |= (detail) ? OSPF_DEBUG_DETAIL : 0; + + for (i = 0; i < 5; i++) + if (type & (0x01 << i)) { + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_PACKET_OFF(i, flag); + else + DEBUG_PACKET_ON(i, flag); + } else { + if (no) + TERM_DEBUG_PACKET_OFF(i, flag); + else + TERM_DEBUG_PACKET_ON(i, flag); + } + } + +#ifdef DEBUG +/* +for (i = 0; i < 5; i++) + zlog_debug ("flag[%d] = %d", i, ospf_debug_packet[i]); +*/ +#endif /* DEBUG */ + + return CMD_SUCCESS; +} + +DEFUN (debug_ospf_ism, + debug_ospf_ism_cmd, + "debug ospf [(1-65535)] ism []", + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF Interface State Machine\n" + "ISM Status Information\n" + "ISM Event Information\n" + "ISM TImer Information\n") +{ + int inst = (argv[2]->type == RANGE_TKN); + char *dbgparam = (argc == 4 + inst) ? argv[argc - 1]->text : NULL; + + if (inst) // user passed instance ID + { + if (inst != ospf_instance) + return CMD_NOT_MY_INSTANCE; + } + + if (vty->node == CONFIG_NODE) { + if (!dbgparam) + DEBUG_ON(ism, ISM); + else { + if (strmatch(dbgparam, "status")) + DEBUG_ON(ism, ISM_STATUS); + else if (strmatch(dbgparam, "events")) + DEBUG_ON(ism, ISM_EVENTS); + else if (strmatch(dbgparam, "timers")) + DEBUG_ON(ism, ISM_TIMERS); + } + + return CMD_SUCCESS; + } + + /* ENABLE_NODE. */ + if (!dbgparam) + TERM_DEBUG_ON(ism, ISM); + else { + if (strmatch(dbgparam, "status")) + TERM_DEBUG_ON(ism, ISM_STATUS); + else if (strmatch(dbgparam, "events")) + TERM_DEBUG_ON(ism, ISM_EVENTS); + else if (strmatch(dbgparam, "timers")) + TERM_DEBUG_ON(ism, ISM_TIMERS); + } + + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf_ism, + no_debug_ospf_ism_cmd, + "no debug ospf [(1-65535)] ism []", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF Interface State Machine\n" + "ISM Status Information\n" + "ISM Event Information\n" + "ISM TImer Information\n") +{ + int inst = (argv[3]->type == RANGE_TKN); + char *dbgparam = (argc == 5 + inst) ? argv[argc - 1]->text : NULL; + + if (inst) // user passed instance ID + { + if (inst != ospf_instance) + return CMD_NOT_MY_INSTANCE; + } + + if (vty->node == CONFIG_NODE) { + if (!dbgparam) + DEBUG_OFF(ism, ISM); + else { + if (strmatch(dbgparam, "status")) + DEBUG_OFF(ism, ISM_STATUS); + else if (strmatch(dbgparam, "events")) + DEBUG_OFF(ism, ISM_EVENTS); + else if (strmatch(dbgparam, "timers")) + DEBUG_OFF(ism, ISM_TIMERS); + } + + return CMD_SUCCESS; + } + + /* ENABLE_NODE. */ + if (!dbgparam) + TERM_DEBUG_OFF(ism, ISM); + else { + if (strmatch(dbgparam, "status")) + TERM_DEBUG_OFF(ism, ISM_STATUS); + else if (strmatch(dbgparam, "events")) + TERM_DEBUG_OFF(ism, ISM_EVENTS); + else if (strmatch(dbgparam, "timers")) + TERM_DEBUG_OFF(ism, ISM_TIMERS); + } + + return CMD_SUCCESS; +} + +static int debug_ospf_nsm_common(struct vty *vty, int arg_base, int argc, + struct cmd_token **argv) +{ + if (vty->node == CONFIG_NODE) { + if (argc == arg_base + 0) + DEBUG_ON(nsm, NSM); + else if (argc == arg_base + 1) { + if (strmatch(argv[arg_base]->text, "status")) + DEBUG_ON(nsm, NSM_STATUS); + else if (strmatch(argv[arg_base]->text, "events")) + DEBUG_ON(nsm, NSM_EVENTS); + else if (strmatch(argv[arg_base]->text, "timers")) + DEBUG_ON(nsm, NSM_TIMERS); + } + + return CMD_SUCCESS; + } + + /* ENABLE_NODE. */ + if (argc == arg_base + 0) + TERM_DEBUG_ON(nsm, NSM); + else if (argc == arg_base + 1) { + if (strmatch(argv[arg_base]->text, "status")) + TERM_DEBUG_ON(nsm, NSM_STATUS); + else if (strmatch(argv[arg_base]->text, "events")) + TERM_DEBUG_ON(nsm, NSM_EVENTS); + else if (strmatch(argv[arg_base]->text, "timers")) + TERM_DEBUG_ON(nsm, NSM_TIMERS); + } + + return CMD_SUCCESS; +} + +DEFUN (debug_ospf_nsm, + debug_ospf_nsm_cmd, + "debug ospf nsm []", + DEBUG_STR + OSPF_STR + "OSPF Neighbor State Machine\n" + "NSM Status Information\n" + "NSM Event Information\n" + "NSM Timer Information\n") +{ + return debug_ospf_nsm_common(vty, 3, argc, argv); +} + +DEFUN (debug_ospf_instance_nsm, + debug_ospf_instance_nsm_cmd, + "debug ospf (1-65535) nsm []", + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF Neighbor State Machine\n" + "NSM Status Information\n" + "NSM Event Information\n" + "NSM Timer Information\n") +{ + int idx_number = 2; + unsigned short instance = 0; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + return debug_ospf_nsm_common(vty, 4, argc, argv); +} + + +static int no_debug_ospf_nsm_common(struct vty *vty, int arg_base, int argc, + struct cmd_token **argv) +{ + /* XXX qlyoung */ + if (vty->node == CONFIG_NODE) { + if (argc == arg_base + 0) + DEBUG_OFF(nsm, NSM); + else if (argc == arg_base + 1) { + if (strmatch(argv[arg_base]->text, "status")) + DEBUG_OFF(nsm, NSM_STATUS); + else if (strmatch(argv[arg_base]->text, "events")) + DEBUG_OFF(nsm, NSM_EVENTS); + else if (strmatch(argv[arg_base]->text, "timers")) + DEBUG_OFF(nsm, NSM_TIMERS); + } + + return CMD_SUCCESS; + } + + /* ENABLE_NODE. */ + if (argc == arg_base + 0) + TERM_DEBUG_OFF(nsm, NSM); + else if (argc == arg_base + 1) { + if (strmatch(argv[arg_base]->text, "status")) + TERM_DEBUG_OFF(nsm, NSM_STATUS); + else if (strmatch(argv[arg_base]->text, "events")) + TERM_DEBUG_OFF(nsm, NSM_EVENTS); + else if (strmatch(argv[arg_base]->text, "timers")) + TERM_DEBUG_OFF(nsm, NSM_TIMERS); + } + + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf_nsm, + no_debug_ospf_nsm_cmd, + "no debug ospf nsm []", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF Neighbor State Machine\n" + "NSM Status Information\n" + "NSM Event Information\n" + "NSM Timer Information\n") +{ + return no_debug_ospf_nsm_common(vty, 4, argc, argv); +} + + +DEFUN (no_debug_ospf_instance_nsm, + no_debug_ospf_instance_nsm_cmd, + "no debug ospf (1-65535) nsm []", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF Neighbor State Machine\n" + "NSM Status Information\n" + "NSM Event Information\n" + "NSM Timer Information\n") +{ + int idx_number = 3; + unsigned short instance = 0; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + return no_debug_ospf_nsm_common(vty, 5, argc, argv); +} + + +static int debug_ospf_lsa_common(struct vty *vty, int arg_base, int argc, + struct cmd_token **argv) +{ + if (vty->node == CONFIG_NODE) { + if (argc == arg_base + 0) + DEBUG_ON(lsa, LSA); + else if (argc == arg_base + 1) { + if (strmatch(argv[arg_base]->text, "generate")) + DEBUG_ON(lsa, LSA_GENERATE); + else if (strmatch(argv[arg_base]->text, "flooding")) + DEBUG_ON(lsa, LSA_FLOODING); + else if (strmatch(argv[arg_base]->text, "install")) + DEBUG_ON(lsa, LSA_INSTALL); + else if (strmatch(argv[arg_base]->text, "refresh")) + DEBUG_ON(lsa, LSA_REFRESH); + else if (strmatch(argv[arg_base]->text, "aggregate")) + DEBUG_ON(lsa, EXTNL_LSA_AGGR); + } + + return CMD_SUCCESS; + } + + /* ENABLE_NODE. */ + if (argc == arg_base + 0) + TERM_DEBUG_ON(lsa, LSA); + else if (argc == arg_base + 1) { + if (strmatch(argv[arg_base]->text, "generate")) + TERM_DEBUG_ON(lsa, LSA_GENERATE); + else if (strmatch(argv[arg_base]->text, "flooding")) + TERM_DEBUG_ON(lsa, LSA_FLOODING); + else if (strmatch(argv[arg_base]->text, "install")) + TERM_DEBUG_ON(lsa, LSA_INSTALL); + else if (strmatch(argv[arg_base]->text, "refresh")) + TERM_DEBUG_ON(lsa, LSA_REFRESH); + else if (strmatch(argv[arg_base]->text, "aggregate")) + TERM_DEBUG_ON(lsa, EXTNL_LSA_AGGR); + } + + return CMD_SUCCESS; +} + +DEFUN (debug_ospf_lsa, + debug_ospf_lsa_cmd, + "debug ospf lsa []", + DEBUG_STR + OSPF_STR + "OSPF Link State Advertisement\n" + "LSA Generation\n" + "LSA Flooding\n" + "LSA Install/Delete\n" + "LSA Refresh\n" + "External LSA Aggregation\n") +{ + return debug_ospf_lsa_common(vty, 3, argc, argv); +} + +DEFUN (debug_ospf_instance_lsa, + debug_ospf_instance_lsa_cmd, + "debug ospf (1-65535) lsa " + "[]", + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF Link State Advertisement\n" + "LSA Generation\n" + "LSA Flooding\n" + "LSA Install/Delete\n" + "LSA Refresh\n" + "External LSA Aggregation\n") +{ + int idx_number = 2; + unsigned short instance = 0; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + return debug_ospf_lsa_common(vty, 4, argc, argv); +} + + +static int no_debug_ospf_lsa_common(struct vty *vty, int arg_base, int argc, + struct cmd_token **argv) +{ + if (vty->node == CONFIG_NODE) { + if (argc == arg_base + 0) + DEBUG_OFF(lsa, LSA); + else if (argc == arg_base + 1) { + if (strmatch(argv[arg_base]->text, "generate")) + DEBUG_OFF(lsa, LSA_GENERATE); + else if (strmatch(argv[arg_base]->text, "flooding")) + DEBUG_OFF(lsa, LSA_FLOODING); + else if (strmatch(argv[arg_base]->text, "install")) + DEBUG_OFF(lsa, LSA_INSTALL); + else if (strmatch(argv[arg_base]->text, "refresh")) + DEBUG_OFF(lsa, LSA_REFRESH); + else if (strmatch(argv[arg_base]->text, "aggregate")) + DEBUG_OFF(lsa, EXTNL_LSA_AGGR); + } + + return CMD_SUCCESS; + } + + /* ENABLE_NODE. */ + if (argc == arg_base + 0) + TERM_DEBUG_OFF(lsa, LSA); + else if (argc == arg_base + 1) { + if (strmatch(argv[arg_base]->text, "generate")) + TERM_DEBUG_OFF(lsa, LSA_GENERATE); + else if (strmatch(argv[arg_base]->text, "flooding")) + TERM_DEBUG_OFF(lsa, LSA_FLOODING); + else if (strmatch(argv[arg_base]->text, "install")) + TERM_DEBUG_OFF(lsa, LSA_INSTALL); + else if (strmatch(argv[arg_base]->text, "refresh")) + TERM_DEBUG_OFF(lsa, LSA_REFRESH); + else if (strmatch(argv[arg_base]->text, "aggregate")) + TERM_DEBUG_OFF(lsa, EXTNL_LSA_AGGR); + } + + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf_lsa, + no_debug_ospf_lsa_cmd, + "no debug ospf lsa []", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF Link State Advertisement\n" + "LSA Generation\n" + "LSA Flooding\n" + "LSA Install/Delete\n" + "LSA Refres\n" + "External LSA Aggregation\n") +{ + return no_debug_ospf_lsa_common(vty, 4, argc, argv); +} + +DEFUN (no_debug_ospf_instance_lsa, + no_debug_ospf_instance_lsa_cmd, + "no debug ospf (1-65535) lsa " + "[]", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF Link State Advertisement\n" + "LSA Generation\n" + "LSA Flooding\n" + "LSA Install/Delete\n" + "LSA Refres\n" + "External LSA Aggregation\n") +{ + int idx_number = 3; + unsigned short instance = 0; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + return no_debug_ospf_lsa_common(vty, 5, argc, argv); +} + + +static int debug_ospf_zebra_common(struct vty *vty, int arg_base, int argc, + struct cmd_token **argv) +{ + if (vty->node == CONFIG_NODE) { + if (argc == arg_base + 0) + DEBUG_ON(zebra, ZEBRA); + else if (argc == arg_base + 1) { + if (strmatch(argv[arg_base]->text, "interface")) + DEBUG_ON(zebra, ZEBRA_INTERFACE); + else if (strmatch(argv[arg_base]->text, "redistribute")) + DEBUG_ON(zebra, ZEBRA_REDISTRIBUTE); + } + + return CMD_SUCCESS; + } + + /* ENABLE_NODE. */ + if (argc == arg_base + 0) + TERM_DEBUG_ON(zebra, ZEBRA); + else if (argc == arg_base + 1) { + if (strmatch(argv[arg_base]->text, "interface")) + TERM_DEBUG_ON(zebra, ZEBRA_INTERFACE); + else if (strmatch(argv[arg_base]->text, "redistribute")) + TERM_DEBUG_ON(zebra, ZEBRA_REDISTRIBUTE); + } + + return CMD_SUCCESS; +} + +DEFUN (debug_ospf_zebra, + debug_ospf_zebra_cmd, + "debug ospf zebra []", + DEBUG_STR + OSPF_STR + ZEBRA_STR + "Zebra interface\n" + "Zebra redistribute\n") +{ + return debug_ospf_zebra_common(vty, 3, argc, argv); +} + +DEFUN (debug_ospf_instance_zebra, + debug_ospf_instance_zebra_cmd, + "debug ospf (1-65535) zebra []", + DEBUG_STR + OSPF_STR + "Instance ID\n" + ZEBRA_STR + "Zebra interface\n" + "Zebra redistribute\n") +{ + int idx_number = 2; + unsigned short instance = 0; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + return debug_ospf_zebra_common(vty, 4, argc, argv); +} + + +static int no_debug_ospf_zebra_common(struct vty *vty, int arg_base, int argc, + struct cmd_token **argv) +{ + if (vty->node == CONFIG_NODE) { + if (argc == arg_base + 0) + DEBUG_OFF(zebra, ZEBRA); + else if (argc == arg_base + 1) { + if (strmatch(argv[arg_base]->text, "interface")) + DEBUG_OFF(zebra, ZEBRA_INTERFACE); + else if (strmatch(argv[arg_base]->text, "redistribute")) + DEBUG_OFF(zebra, ZEBRA_REDISTRIBUTE); + } + + return CMD_SUCCESS; + } + + /* ENABLE_NODE. */ + if (argc == arg_base + 0) + TERM_DEBUG_OFF(zebra, ZEBRA); + else if (argc == arg_base + 1) { + if (strmatch(argv[arg_base]->text, "interface")) + TERM_DEBUG_OFF(zebra, ZEBRA_INTERFACE); + else if (strmatch(argv[arg_base]->text, "redistribute")) + TERM_DEBUG_OFF(zebra, ZEBRA_REDISTRIBUTE); + } + + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf_zebra, + no_debug_ospf_zebra_cmd, + "no debug ospf zebra []", + NO_STR + DEBUG_STR + OSPF_STR + ZEBRA_STR + "Zebra interface\n" + "Zebra redistribute\n") +{ + return no_debug_ospf_zebra_common(vty, 4, argc, argv); +} + +DEFUN (no_debug_ospf_instance_zebra, + no_debug_ospf_instance_zebra_cmd, + "no debug ospf (1-65535) zebra []", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + ZEBRA_STR + "Zebra interface\n" + "Zebra redistribute\n") +{ + int idx_number = 3; + unsigned short instance = 0; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + return no_debug_ospf_zebra_common(vty, 5, argc, argv); +} + + +DEFUN (debug_ospf_event, + debug_ospf_event_cmd, + "debug ospf event", + DEBUG_STR + OSPF_STR + "OSPF event information\n") +{ + if (vty->node == CONFIG_NODE) + CONF_DEBUG_ON(event, EVENT); + TERM_DEBUG_ON(event, EVENT); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf_event, + no_debug_ospf_event_cmd, + "no debug ospf event", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF event information\n") +{ + if (vty->node == CONFIG_NODE) + CONF_DEBUG_OFF(event, EVENT); + TERM_DEBUG_OFF(event, EVENT); + return CMD_SUCCESS; +} + +DEFUN (debug_ospf_instance_event, + debug_ospf_instance_event_cmd, + "debug ospf (1-65535) event", + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF event information\n") +{ + int idx_number = 2; + unsigned short instance = 0; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) + CONF_DEBUG_ON(event, EVENT); + TERM_DEBUG_ON(event, EVENT); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf_instance_event, + no_debug_ospf_instance_event_cmd, + "no debug ospf (1-65535) event", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF event information\n") +{ + int idx_number = 3; + unsigned short instance = 0; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) + CONF_DEBUG_OFF(event, EVENT); + TERM_DEBUG_OFF(event, EVENT); + return CMD_SUCCESS; +} + +DEFUN (debug_ospf_nssa, + debug_ospf_nssa_cmd, + "debug ospf nssa", + DEBUG_STR + OSPF_STR + "OSPF nssa information\n") +{ + if (vty->node == CONFIG_NODE) + CONF_DEBUG_ON(nssa, NSSA); + TERM_DEBUG_ON(nssa, NSSA); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf_nssa, + no_debug_ospf_nssa_cmd, + "no debug ospf nssa", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF nssa information\n") +{ + if (vty->node == CONFIG_NODE) + CONF_DEBUG_OFF(nssa, NSSA); + TERM_DEBUG_OFF(nssa, NSSA); + return CMD_SUCCESS; +} + +DEFUN (debug_ospf_instance_nssa, + debug_ospf_instance_nssa_cmd, + "debug ospf (1-65535) nssa", + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF nssa information\n") +{ + int idx_number = 2; + unsigned short instance = 0; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) + CONF_DEBUG_ON(nssa, NSSA); + TERM_DEBUG_ON(nssa, NSSA); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf_instance_nssa, + no_debug_ospf_instance_nssa_cmd, + "no debug ospf (1-65535) nssa", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF nssa information\n") +{ + int idx_number = 3; + unsigned short instance = 0; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) + CONF_DEBUG_OFF(nssa, NSSA); + TERM_DEBUG_OFF(nssa, NSSA); + return CMD_SUCCESS; +} + +DEFPY (debug_ospf_te, + debug_ospf_te_cmd, + "[no$no] debug ospf [(1-65535)$instance] te", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF-TE information\n") +{ + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(te, TE); + else + DEBUG_ON(te, TE); + } else { + if (no) + TERM_DEBUG_OFF(te, TE); + else + TERM_DEBUG_ON(te, TE); + } + + return CMD_SUCCESS; +} + +DEFPY (debug_ospf_sr, + debug_ospf_sr_cmd, + "[no$no] debug ospf [(1-65535)$instance] sr", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF-SR information\n") +{ + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(sr, SR); + else + DEBUG_ON(sr, SR); + } else { + if (no) + TERM_DEBUG_OFF(sr, SR); + else + TERM_DEBUG_ON(sr, SR); + } + + return CMD_SUCCESS; +} + +DEFPY (debug_ospf_ti_lfa, + debug_ospf_ti_lfa_cmd, + "[no$no] debug ospf [(1-65535)$instance] ti-lfa", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF-SR TI-LFA information\n") +{ + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(ti_lfa, TI_LFA); + else + DEBUG_ON(ti_lfa, TI_LFA); + } else { + if (no) + TERM_DEBUG_OFF(ti_lfa, TI_LFA); + else + TERM_DEBUG_ON(ti_lfa, TI_LFA); + } + + return CMD_SUCCESS; +} + +DEFPY (debug_ospf_default_info, + debug_ospf_default_info_cmd, + "[no$no] debug ospf [(1-65535)$instance] default-information", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF default information\n") +{ + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(defaultinfo, DEFAULTINFO); + else + DEBUG_ON(defaultinfo, DEFAULTINFO); + } else { + if (no) + TERM_DEBUG_OFF(defaultinfo, DEFAULTINFO); + else + TERM_DEBUG_ON(defaultinfo, DEFAULTINFO); + } + + return CMD_SUCCESS; +} + +DEFPY (debug_ospf_ldp_sync, + debug_ospf_ldp_sync_cmd, + "[no$no] debug ospf [(1-65535)$instance] ldp-sync", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF LDP-Sync information\n") +{ + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(ldp_sync, LDP_SYNC); + else + DEBUG_ON(ldp_sync, LDP_SYNC); + } else { + if (no) + TERM_DEBUG_OFF(ldp_sync, LDP_SYNC); + else + TERM_DEBUG_ON(ldp_sync, LDP_SYNC); + } + + return CMD_SUCCESS; +} + +DEFPY (debug_ospf_gr, + debug_ospf_gr_cmd, + "[no$no] debug ospf [(1-65535)$instance] graceful-restart", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF Graceful Restart\n") +{ + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) { + if (no) + CONF_DEBUG_OFF(gr, GR); + else + CONF_DEBUG_ON(gr, GR); + } + + if (no) + TERM_DEBUG_OFF(gr, GR); + else + TERM_DEBUG_ON(gr, GR); + + return CMD_SUCCESS; +} + +DEFPY (debug_ospf_bfd, + debug_ospf_bfd_cmd, + "[no] debug ospf [(1-65535)$instance] bfd", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "Bidirection Forwarding Detection\n") +{ + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) { + if (no) { + bfd_protocol_integration_set_debug(false); + DEBUG_OFF(bfd, BFD_LIB); + } else { + bfd_protocol_integration_set_debug(true); + DEBUG_ON(bfd, BFD_LIB); + } + } else { + if (no) + TERM_DEBUG_OFF(bfd, BFD_LIB); + else + TERM_DEBUG_ON(bfd, BFD_LIB); + } + + return CMD_SUCCESS; +} + +DEFPY (debug_ospf_client_api, + debug_ospf_client_api_cmd, + "[no$no] debug ospf [(1-65535)$instance] client-api", + NO_STR + DEBUG_STR + OSPF_STR + "Instance ID\n" + "OSPF client API information\n") +{ + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + if (vty->node == CONFIG_NODE) { + if (no) + DEBUG_OFF(client_api, CLIENT_API); + else + DEBUG_ON(client_api, CLIENT_API); + } else { + if (no) + TERM_DEBUG_OFF(client_api, CLIENT_API); + else + TERM_DEBUG_ON(client_api, CLIENT_API); + } + + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf, + no_debug_ospf_cmd, + "no debug ospf", + NO_STR + DEBUG_STR + OSPF_STR) +{ + int flag = OSPF_DEBUG_SEND | OSPF_DEBUG_RECV | OSPF_DEBUG_DETAIL; + int i; + + if (vty->node == CONFIG_NODE) { + CONF_DEBUG_OFF(event, EVENT); + CONF_DEBUG_OFF(nssa, NSSA); + DEBUG_OFF(ism, ISM_EVENTS); + DEBUG_OFF(ism, ISM_STATUS); + DEBUG_OFF(ism, ISM_TIMERS); + DEBUG_OFF(lsa, LSA); + DEBUG_OFF(lsa, LSA_FLOODING); + DEBUG_OFF(lsa, LSA_GENERATE); + DEBUG_OFF(lsa, LSA_INSTALL); + DEBUG_OFF(lsa, LSA_REFRESH); + DEBUG_OFF(nsm, NSM); + DEBUG_OFF(nsm, NSM_EVENTS); + DEBUG_OFF(nsm, NSM_STATUS); + DEBUG_OFF(nsm, NSM_TIMERS); + DEBUG_OFF(event, EVENT); + DEBUG_OFF(zebra, ZEBRA); + DEBUG_OFF(zebra, ZEBRA_INTERFACE); + DEBUG_OFF(zebra, ZEBRA_REDISTRIBUTE); + DEBUG_OFF(defaultinfo, DEFAULTINFO); + DEBUG_OFF(ldp_sync, LDP_SYNC); + DEBUG_OFF(te, TE); + DEBUG_OFF(sr, SR); + DEBUG_OFF(ti_lfa, TI_LFA); + DEBUG_OFF(client_api, CLIENT_API); + + /* BFD debugging is two parts: OSPF and library. */ + DEBUG_OFF(bfd, BFD_LIB); + bfd_protocol_integration_set_debug(false); + + for (i = 0; i < 5; i++) + DEBUG_PACKET_OFF(i, flag); + } + + for (i = 0; i < 5; i++) + TERM_DEBUG_PACKET_OFF(i, flag); + + TERM_DEBUG_OFF(event, EVENT); + TERM_DEBUG_OFF(ism, ISM); + TERM_DEBUG_OFF(ism, ISM_EVENTS); + TERM_DEBUG_OFF(ism, ISM_STATUS); + TERM_DEBUG_OFF(ism, ISM_TIMERS); + TERM_DEBUG_OFF(lsa, LSA); + TERM_DEBUG_OFF(lsa, LSA_FLOODING); + TERM_DEBUG_OFF(lsa, LSA_GENERATE); + TERM_DEBUG_OFF(lsa, LSA_INSTALL); + TERM_DEBUG_OFF(lsa, LSA_REFRESH); + TERM_DEBUG_OFF(nsm, NSM); + TERM_DEBUG_OFF(nsm, NSM_EVENTS); + TERM_DEBUG_OFF(nsm, NSM_STATUS); + TERM_DEBUG_OFF(nsm, NSM_TIMERS); + TERM_DEBUG_OFF(nssa, NSSA); + TERM_DEBUG_OFF(zebra, ZEBRA); + TERM_DEBUG_OFF(zebra, ZEBRA_INTERFACE); + TERM_DEBUG_OFF(zebra, ZEBRA_REDISTRIBUTE); + TERM_DEBUG_OFF(defaultinfo, DEFAULTINFO); + TERM_DEBUG_OFF(ldp_sync, LDP_SYNC); + TERM_DEBUG_OFF(te, TE); + TERM_DEBUG_OFF(sr, SR); + TERM_DEBUG_OFF(ti_lfa, TI_LFA); + TERM_DEBUG_OFF(bfd, BFD_LIB); + TERM_DEBUG_OFF(client_api, CLIENT_API); + + return CMD_SUCCESS; +} + +static int show_debugging_ospf_common(struct vty *vty) +{ + int i; + + if (ospf_instance) + vty_out(vty, "\nOSPF Instance: %d\n\n", ospf_instance); + + vty_out(vty, "OSPF debugging status:\n"); + + /* Show debug status for events. */ + if (IS_DEBUG_OSPF(event, EVENT)) + vty_out(vty, " OSPF event debugging is on\n"); + + /* Show debug status for ISM. */ + if (IS_DEBUG_OSPF(ism, ISM) == OSPF_DEBUG_ISM) + vty_out(vty, " OSPF ISM debugging is on\n"); + else { + if (IS_DEBUG_OSPF(ism, ISM_STATUS)) + vty_out(vty, " OSPF ISM status debugging is on\n"); + if (IS_DEBUG_OSPF(ism, ISM_EVENTS)) + vty_out(vty, " OSPF ISM event debugging is on\n"); + if (IS_DEBUG_OSPF(ism, ISM_TIMERS)) + vty_out(vty, " OSPF ISM timer debugging is on\n"); + } + + /* Show debug status for NSM. */ + if (IS_DEBUG_OSPF(nsm, NSM) == OSPF_DEBUG_NSM) + vty_out(vty, " OSPF NSM debugging is on\n"); + else { + if (IS_DEBUG_OSPF(nsm, NSM_STATUS)) + vty_out(vty, " OSPF NSM status debugging is on\n"); + if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) + vty_out(vty, " OSPF NSM event debugging is on\n"); + if (IS_DEBUG_OSPF(nsm, NSM_TIMERS)) + vty_out(vty, " OSPF NSM timer debugging is on\n"); + } + + /* Show debug status for OSPF Packets. */ + for (i = 0; i < 5; i++) + if (IS_DEBUG_OSPF_PACKET(i, SEND) + && IS_DEBUG_OSPF_PACKET(i, RECV)) { + vty_out(vty, " OSPF packet %s%s debugging is on\n", + lookup_msg(ospf_packet_type_str, i + 1, NULL), + IS_DEBUG_OSPF_PACKET(i, DETAIL) ? " detail" + : ""); + } else { + if (IS_DEBUG_OSPF_PACKET(i, SEND)) + vty_out(vty, + " OSPF packet %s send%s debugging is on\n", + lookup_msg(ospf_packet_type_str, i + 1, + NULL), + IS_DEBUG_OSPF_PACKET(i, DETAIL) + ? " detail" + : ""); + if (IS_DEBUG_OSPF_PACKET(i, RECV)) + vty_out(vty, + " OSPF packet %s receive%s debugging is on\n", + lookup_msg(ospf_packet_type_str, i + 1, + NULL), + IS_DEBUG_OSPF_PACKET(i, DETAIL) + ? " detail" + : ""); + } + + /* Show debug status for OSPF LSAs. */ + if (IS_DEBUG_OSPF(lsa, LSA) == OSPF_DEBUG_LSA) + vty_out(vty, " OSPF LSA debugging is on\n"); + else { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + vty_out(vty, " OSPF LSA generation debugging is on\n"); + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + vty_out(vty, " OSPF LSA flooding debugging is on\n"); + if (IS_DEBUG_OSPF(lsa, LSA_INSTALL)) + vty_out(vty, " OSPF LSA install debugging is on\n"); + if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) + vty_out(vty, " OSPF LSA refresh debugging is on\n"); + } + + /* Show debug status for Zebra. */ + if (IS_DEBUG_OSPF(zebra, ZEBRA) == OSPF_DEBUG_ZEBRA) + vty_out(vty, " OSPF Zebra debugging is on\n"); + else { + if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + vty_out(vty, + " OSPF Zebra interface debugging is on\n"); + if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) + vty_out(vty, + " OSPF Zebra redistribute debugging is on\n"); + } + + if (IS_DEBUG_OSPF(defaultinfo, DEFAULTINFO) == OSPF_DEBUG_DEFAULTINFO) + vty_out(vty, " OSPF default information is on\n"); + + /* Show debug status for NSSA. */ + if (IS_DEBUG_OSPF(nssa, NSSA) == OSPF_DEBUG_NSSA) + vty_out(vty, " OSPF NSSA debugging is on\n"); + + /* Show debug status for LDP-SYNC. */ + if (IS_DEBUG_OSPF(ldp_sync, LDP_SYNC) == OSPF_DEBUG_LDP_SYNC) + vty_out(vty, " OSPF ldp-sync debugging is on\n"); + + /* Show debug status for GR. */ + if (IS_DEBUG_OSPF(gr, GR) == OSPF_DEBUG_GR) + vty_out(vty, " OSPF Graceful Restart debugging is on\n"); + + /* Show debug status for TE */ + if (IS_DEBUG_OSPF(te, TE) == OSPF_DEBUG_TE) + vty_out(vty, " OSPF TE debugging is on\n"); + + /* Show debug status for SR */ + if (IS_DEBUG_OSPF(sr, SR) == OSPF_DEBUG_SR) + vty_out(vty, " OSPF SR debugging is on\n"); + + /* Show debug status for TI-LFA */ + if (IS_DEBUG_OSPF(ti_lfa, TI_LFA) == OSPF_DEBUG_TI_LFA) + vty_out(vty, " OSPF TI-LFA debugging is on\n"); + + if (IS_DEBUG_OSPF(bfd, BFD_LIB) == OSPF_DEBUG_BFD_LIB) + vty_out(vty, + " OSPF BFD integration library debugging is on\n"); + + /* Show debug status for LDP-SYNC. */ + if (IS_DEBUG_OSPF(client_api, CLIENT_API) == OSPF_DEBUG_CLIENT_API) + vty_out(vty, " OSPF client-api debugging is on\n"); + + return CMD_SUCCESS; +} + +DEFUN_NOSH (show_debugging_ospf, + show_debugging_ospf_cmd, + "show debugging [ospf]", + SHOW_STR + DEBUG_STR + OSPF_STR) +{ + show_debugging_ospf_common(vty); + + cmd_show_lib_debugs(vty); + + return CMD_SUCCESS; +} + +DEFUN_NOSH (show_debugging_ospf_instance, + show_debugging_ospf_instance_cmd, + "show debugging ospf (1-65535)", + SHOW_STR + DEBUG_STR + OSPF_STR + "Instance ID\n") +{ + int idx_number = 3; + unsigned short instance = 0; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + show_debugging_ospf_common(vty); + + cmd_show_lib_debugs(vty); + + return CMD_SUCCESS; +} + +static int config_write_debug(struct vty *vty); +/* Debug node. */ +static struct cmd_node debug_node = { + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = config_write_debug, +}; + +static int config_write_debug(struct vty *vty) +{ + int write = 0; + int i, r; + + const char *type_str[] = {"hello", "dd", "ls-request", "ls-update", + "ls-ack"}; + const char *detail_str[] = { + "", " send", " recv", "", + " detail", " send detail", " recv detail", " detail"}; + + char str[16]; + memset(str, 0, 16); + + if (ospf_instance) + snprintf(str, sizeof(str), " %u", ospf_instance); + + /* debug ospf ism (status|events|timers). */ + if (IS_CONF_DEBUG_OSPF(ism, ISM) == OSPF_DEBUG_ISM) + vty_out(vty, "debug ospf%s ism\n", str); + else { + if (IS_CONF_DEBUG_OSPF(ism, ISM_STATUS)) + vty_out(vty, "debug ospf%s ism status\n", str); + if (IS_CONF_DEBUG_OSPF(ism, ISM_EVENTS)) + vty_out(vty, "debug ospf%s ism event\n", str); + if (IS_CONF_DEBUG_OSPF(ism, ISM_TIMERS)) + vty_out(vty, "debug ospf%s ism timer\n", str); + } + + /* debug ospf nsm (status|events|timers). */ + if (IS_CONF_DEBUG_OSPF(nsm, NSM) == OSPF_DEBUG_NSM) + vty_out(vty, "debug ospf%s nsm\n", str); + else { + if (IS_CONF_DEBUG_OSPF(nsm, NSM_STATUS)) + vty_out(vty, "debug ospf%s nsm status\n", str); + if (IS_CONF_DEBUG_OSPF(nsm, NSM_EVENTS)) + vty_out(vty, "debug ospf%s nsm event\n", str); + if (IS_CONF_DEBUG_OSPF(nsm, NSM_TIMERS)) + vty_out(vty, "debug ospf%s nsm timer\n", str); + } + + /* debug ospf lsa (generate|flooding|install|refresh). */ + if (IS_CONF_DEBUG_OSPF(lsa, LSA) == OSPF_DEBUG_LSA) + vty_out(vty, "debug ospf%s lsa\n", str); + else { + if (IS_CONF_DEBUG_OSPF(lsa, LSA_GENERATE)) + vty_out(vty, "debug ospf%s lsa generate\n", str); + if (IS_CONF_DEBUG_OSPF(lsa, LSA_FLOODING)) + vty_out(vty, "debug ospf%s lsa flooding\n", str); + if (IS_CONF_DEBUG_OSPF(lsa, LSA_INSTALL)) + vty_out(vty, "debug ospf%s lsa install\n", str); + if (IS_CONF_DEBUG_OSPF(lsa, LSA_REFRESH)) + vty_out(vty, "debug ospf%s lsa refresh\n", str); + + write = 1; + } + + /* debug ospf zebra (interface|redistribute). */ + if (IS_CONF_DEBUG_OSPF(zebra, ZEBRA) == OSPF_DEBUG_ZEBRA) + vty_out(vty, "debug ospf%s zebra\n", str); + else { + if (IS_CONF_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + vty_out(vty, "debug ospf%s zebra interface\n", str); + if (IS_CONF_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) + vty_out(vty, "debug ospf%s zebra redistribute\n", str); + + write = 1; + } + + /* debug ospf event. */ + if (IS_CONF_DEBUG_OSPF(event, EVENT) == OSPF_DEBUG_EVENT) { + vty_out(vty, "debug ospf%s event\n", str); + write = 1; + } + + /* debug ospf nssa. */ + if (IS_CONF_DEBUG_OSPF(nssa, NSSA) == OSPF_DEBUG_NSSA) { + vty_out(vty, "debug ospf%s nssa\n", str); + write = 1; + } + + /* debug ospf packet all detail. */ + r = OSPF_DEBUG_SEND_RECV | OSPF_DEBUG_DETAIL; + for (i = 0; i < 5; i++) + r &= conf_debug_ospf_packet[i] + & (OSPF_DEBUG_SEND_RECV | OSPF_DEBUG_DETAIL); + if (r == (OSPF_DEBUG_SEND_RECV | OSPF_DEBUG_DETAIL)) { + vty_out(vty, "debug ospf%s packet all detail\n", str); + write = 1; + } + + /* debug ospf packet all. */ + r = OSPF_DEBUG_SEND_RECV; + for (i = 0; i < 5; i++) + r &= conf_debug_ospf_packet[i] & OSPF_DEBUG_SEND_RECV; + if (r == OSPF_DEBUG_SEND_RECV) { + vty_out(vty, "debug ospf%s packet all\n", str); + for (i = 0; i < 5; i++) + if (conf_debug_ospf_packet[i] & OSPF_DEBUG_DETAIL) + vty_out(vty, "debug ospf%s packet %s detail\n", + str, type_str[i]); + write = 1; + } + + /* debug ospf packet (hello|dd|ls-request|ls-update|ls-ack) + (send|recv) (detail). */ + for (i = 0; i < 5; i++) { + if (conf_debug_ospf_packet[i] == 0) + continue; + + vty_out(vty, "debug ospf%s packet %s%s\n", str, type_str[i], + detail_str[conf_debug_ospf_packet[i]]); + write = 1; + } + + /* debug ospf te */ + if (IS_CONF_DEBUG_OSPF(te, TE) == OSPF_DEBUG_TE) { + vty_out(vty, "debug ospf%s te\n", str); + write = 1; + } + + /* debug ospf sr */ + if (IS_CONF_DEBUG_OSPF(sr, SR) == OSPF_DEBUG_SR) { + vty_out(vty, "debug ospf%s sr\n", str); + write = 1; + } + + /* debug ospf sr ti-lfa */ + if (IS_CONF_DEBUG_OSPF(ti_lfa, TI_LFA) == OSPF_DEBUG_TI_LFA) { + vty_out(vty, "debug ospf%s ti-lfa\n", str); + write = 1; + } + + /* debug ospf ldp-sync */ + if (IS_CONF_DEBUG_OSPF(ldp_sync, LDP_SYNC) == OSPF_DEBUG_LDP_SYNC) { + vty_out(vty, "debug ospf%s ldp-sync\n", str); + write = 1; + } + + /* debug ospf gr */ + if (IS_CONF_DEBUG_OSPF(gr, GR) == OSPF_DEBUG_GR) { + vty_out(vty, "debug ospf%s graceful-restart\n", str); + write = 1; + } + + if (IS_CONF_DEBUG_OSPF(bfd, BFD_LIB) == OSPF_DEBUG_BFD_LIB) { + vty_out(vty, "debug ospf%s bfd\n", str); + write = 1; + } + + /* debug ospf client-api */ + if (IS_CONF_DEBUG_OSPF(client_api, CLIENT_API) == + OSPF_DEBUG_CLIENT_API) { + vty_out(vty, "debug ospf%s client-api\n", str); + write = 1; + } + + /* debug ospf default-information */ + if (IS_CONF_DEBUG_OSPF(defaultinfo, DEFAULTINFO) == + OSPF_DEBUG_DEFAULTINFO) { + vty_out(vty, "debug ospf%s default-information\n", str); + write = 1; + } + + return write; +} + +/* Initialize debug commands. */ +void ospf_debug_init(void) +{ + install_node(&debug_node); + + install_element(ENABLE_NODE, &show_debugging_ospf_cmd); + install_element(ENABLE_NODE, &debug_ospf_ism_cmd); + install_element(ENABLE_NODE, &debug_ospf_nsm_cmd); + install_element(ENABLE_NODE, &debug_ospf_lsa_cmd); + install_element(ENABLE_NODE, &debug_ospf_zebra_cmd); + install_element(ENABLE_NODE, &debug_ospf_event_cmd); + install_element(ENABLE_NODE, &debug_ospf_nssa_cmd); + install_element(ENABLE_NODE, &debug_ospf_te_cmd); + install_element(ENABLE_NODE, &debug_ospf_sr_cmd); + install_element(ENABLE_NODE, &debug_ospf_ti_lfa_cmd); + install_element(ENABLE_NODE, &debug_ospf_default_info_cmd); + install_element(ENABLE_NODE, &debug_ospf_ldp_sync_cmd); + install_element(ENABLE_NODE, &debug_ospf_client_api_cmd); + install_element(ENABLE_NODE, &no_debug_ospf_ism_cmd); + install_element(ENABLE_NODE, &no_debug_ospf_nsm_cmd); + install_element(ENABLE_NODE, &no_debug_ospf_lsa_cmd); + install_element(ENABLE_NODE, &no_debug_ospf_zebra_cmd); + install_element(ENABLE_NODE, &no_debug_ospf_event_cmd); + install_element(ENABLE_NODE, &no_debug_ospf_nssa_cmd); + install_element(ENABLE_NODE, &debug_ospf_gr_cmd); + install_element(ENABLE_NODE, &debug_ospf_bfd_cmd); + + install_element(ENABLE_NODE, &show_debugging_ospf_instance_cmd); + install_element(ENABLE_NODE, &debug_ospf_packet_cmd); + + install_element(ENABLE_NODE, &debug_ospf_instance_nsm_cmd); + install_element(ENABLE_NODE, &debug_ospf_instance_lsa_cmd); + install_element(ENABLE_NODE, &debug_ospf_instance_zebra_cmd); + install_element(ENABLE_NODE, &debug_ospf_instance_event_cmd); + install_element(ENABLE_NODE, &debug_ospf_instance_nssa_cmd); + install_element(ENABLE_NODE, &no_debug_ospf_instance_nsm_cmd); + install_element(ENABLE_NODE, &no_debug_ospf_instance_lsa_cmd); + install_element(ENABLE_NODE, &no_debug_ospf_instance_zebra_cmd); + install_element(ENABLE_NODE, &no_debug_ospf_instance_event_cmd); + install_element(ENABLE_NODE, &no_debug_ospf_instance_nssa_cmd); + install_element(ENABLE_NODE, &no_debug_ospf_cmd); + + install_element(CONFIG_NODE, &debug_ospf_packet_cmd); + install_element(CONFIG_NODE, &debug_ospf_ism_cmd); + install_element(CONFIG_NODE, &no_debug_ospf_ism_cmd); + + install_element(CONFIG_NODE, &debug_ospf_nsm_cmd); + install_element(CONFIG_NODE, &debug_ospf_lsa_cmd); + install_element(CONFIG_NODE, &debug_ospf_zebra_cmd); + install_element(CONFIG_NODE, &debug_ospf_event_cmd); + install_element(CONFIG_NODE, &debug_ospf_nssa_cmd); + install_element(CONFIG_NODE, &debug_ospf_te_cmd); + install_element(CONFIG_NODE, &debug_ospf_sr_cmd); + install_element(CONFIG_NODE, &debug_ospf_ti_lfa_cmd); + install_element(CONFIG_NODE, &debug_ospf_default_info_cmd); + install_element(CONFIG_NODE, &debug_ospf_ldp_sync_cmd); + install_element(CONFIG_NODE, &debug_ospf_client_api_cmd); + install_element(CONFIG_NODE, &no_debug_ospf_nsm_cmd); + install_element(CONFIG_NODE, &no_debug_ospf_lsa_cmd); + install_element(CONFIG_NODE, &no_debug_ospf_zebra_cmd); + install_element(CONFIG_NODE, &no_debug_ospf_event_cmd); + install_element(CONFIG_NODE, &no_debug_ospf_nssa_cmd); + install_element(CONFIG_NODE, &debug_ospf_gr_cmd); + install_element(CONFIG_NODE, &debug_ospf_bfd_cmd); + + install_element(CONFIG_NODE, &debug_ospf_instance_nsm_cmd); + install_element(CONFIG_NODE, &debug_ospf_instance_lsa_cmd); + install_element(CONFIG_NODE, &debug_ospf_instance_zebra_cmd); + install_element(CONFIG_NODE, &debug_ospf_instance_event_cmd); + install_element(CONFIG_NODE, &debug_ospf_instance_nssa_cmd); + install_element(CONFIG_NODE, &no_debug_ospf_instance_nsm_cmd); + install_element(CONFIG_NODE, &no_debug_ospf_instance_lsa_cmd); + install_element(CONFIG_NODE, &no_debug_ospf_instance_zebra_cmd); + install_element(CONFIG_NODE, &no_debug_ospf_instance_event_cmd); + install_element(CONFIG_NODE, &no_debug_ospf_instance_nssa_cmd); + install_element(CONFIG_NODE, &no_debug_ospf_cmd); +} diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h new file mode 100644 index 0000000..0d47be2 --- /dev/null +++ b/ospfd/ospf_dump.h @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPFd dump routine. + * Copyright (C) 1999 Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPF_DUMP_H +#define _ZEBRA_OSPF_DUMP_H + +/* Debug Flags. */ +#define OSPF_DEBUG_HELLO 0x01 +#define OSPF_DEBUG_DB_DESC 0x02 +#define OSPF_DEBUG_LS_REQ 0x04 +#define OSPF_DEBUG_LS_UPD 0x08 +#define OSPF_DEBUG_LS_ACK 0x10 +#define OSPF_DEBUG_ALL 0x1f + +#define OSPF_DEBUG_SEND 0x01 +#define OSPF_DEBUG_RECV 0x02 +#define OSPF_DEBUG_SEND_RECV 0x03 +#define OSPF_DEBUG_DETAIL 0x04 + +#define OSPF_DEBUG_ISM_STATUS 0x01 +#define OSPF_DEBUG_ISM_EVENTS 0x02 +#define OSPF_DEBUG_ISM_TIMERS 0x04 +#define OSPF_DEBUG_ISM 0x07 +#define OSPF_DEBUG_NSM_STATUS 0x01 +#define OSPF_DEBUG_NSM_EVENTS 0x02 +#define OSPF_DEBUG_NSM_TIMERS 0x04 +#define OSPF_DEBUG_NSM 0x07 + +#define OSPF_DEBUG_LSA_GENERATE 0x01 +#define OSPF_DEBUG_LSA_FLOODING 0x02 +#define OSPF_DEBUG_LSA_INSTALL 0x04 +#define OSPF_DEBUG_LSA_REFRESH 0x08 +#define OSPF_DEBUG_LSA 0x0F +#define OSPF_DEBUG_EXTNL_LSA_AGGR 0x10 + +#define OSPF_DEBUG_ZEBRA_INTERFACE 0x01 +#define OSPF_DEBUG_ZEBRA_REDISTRIBUTE 0x02 +#define OSPF_DEBUG_ZEBRA 0x03 + +#define OSPF_DEBUG_EVENT 0x01 +#define OSPF_DEBUG_NSSA 0x02 +#define OSPF_DEBUG_TE 0x04 +#define OSPF_DEBUG_EXT 0x08 +#define OSPF_DEBUG_SR 0x10 +#define OSPF_DEBUG_TI_LFA 0x11 +#define OSPF_DEBUG_DEFAULTINFO 0x20 +#define OSPF_DEBUG_LDP_SYNC 0x40 + +#define OSPF_DEBUG_GR 0x01 + +#define OSPF_DEBUG_BFD_LIB 0x01 + +#define OSPF_DEBUG_CLIENT_API 0x01 + +/* Macro for setting debug option. */ +#define CONF_DEBUG_PACKET_ON(a, b) conf_debug_ospf_packet[a] |= (b) +#define CONF_DEBUG_PACKET_OFF(a, b) conf_debug_ospf_packet[a] &= ~(b) +#define TERM_DEBUG_PACKET_ON(a, b) term_debug_ospf_packet[a] |= (b) +#define TERM_DEBUG_PACKET_OFF(a, b) term_debug_ospf_packet[a] &= ~(b) +#define DEBUG_PACKET_ON(a, b) \ + do { \ + CONF_DEBUG_PACKET_ON(a, b); \ + TERM_DEBUG_PACKET_ON(a, b); \ + } while (0) +#define DEBUG_PACKET_OFF(a, b) \ + do { \ + CONF_DEBUG_PACKET_OFF(a, b); \ + TERM_DEBUG_PACKET_OFF(a, b); \ + } while (0) + +#define CONF_DEBUG_ON(a, b) conf_debug_ospf_ ## a |= (OSPF_DEBUG_ ## b) +#define CONF_DEBUG_OFF(a, b) conf_debug_ospf_ ## a &= ~(OSPF_DEBUG_ ## b) +#define TERM_DEBUG_ON(a, b) term_debug_ospf_ ## a |= (OSPF_DEBUG_ ## b) +#define TERM_DEBUG_OFF(a, b) term_debug_ospf_ ## a &= ~(OSPF_DEBUG_ ## b) +#define DEBUG_ON(a, b) \ + do { \ + CONF_DEBUG_ON(a, b); \ + TERM_DEBUG_ON(a, b); \ + } while (0) +#define DEBUG_OFF(a, b) \ + do { \ + CONF_DEBUG_OFF(a, b); \ + TERM_DEBUG_OFF(a, b); \ + } while (0) + +/* Macro for checking debug option. */ +#define IS_DEBUG_OSPF_PACKET(a, b) (term_debug_ospf_packet[a] & OSPF_DEBUG_##b) +#define IS_DEBUG_OSPF(a, b) (term_debug_ospf_##a & OSPF_DEBUG_##b) +#define IS_DEBUG_OSPF_EVENT IS_DEBUG_OSPF(event, EVENT) + +#define IS_DEBUG_OSPF_NSSA IS_DEBUG_OSPF(nssa, NSSA) + +#define IS_DEBUG_OSPF_TE IS_DEBUG_OSPF(te, TE) + +#define IS_DEBUG_OSPF_EXT IS_DEBUG_OSPF(ext, EXT) + +#define IS_DEBUG_OSPF_SR IS_DEBUG_OSPF(sr, SR) + +#define IS_DEBUG_OSPF_TI_LFA IS_DEBUG_OSPF(ti_lfa, TI_LFA) + +#define IS_DEBUG_OSPF_DEFAULT_INFO IS_DEBUG_OSPF(defaultinfo, DEFAULTINFO) + +#define IS_DEBUG_OSPF_LDP_SYNC IS_DEBUG_OSPF(ldp_sync, LDP_SYNC) +#define IS_DEBUG_OSPF_GR IS_DEBUG_OSPF(gr, GR) +#define IS_DEBUG_OSPF_CLIENT_API IS_DEBUG_OSPF(client_api, CLIENT_API) + +#define IS_CONF_DEBUG_OSPF_PACKET(a, b) \ + (conf_debug_ospf_packet[a] & OSPF_DEBUG_##b) +#define IS_CONF_DEBUG_OSPF(a, b) (conf_debug_ospf_##a & OSPF_DEBUG_##b) + +#define AREA_NAME(A) ospf_area_name_string ((A)) +#define IF_NAME(I) ospf_if_name_string ((I)) + +/* Extern debug flag. */ +extern unsigned long term_debug_ospf_packet[]; +extern unsigned long term_debug_ospf_event; +extern unsigned long term_debug_ospf_ism; +extern unsigned long term_debug_ospf_nsm; +extern unsigned long term_debug_ospf_lsa; +extern unsigned long term_debug_ospf_zebra; +extern unsigned long term_debug_ospf_nssa; +extern unsigned long term_debug_ospf_te; +extern unsigned long term_debug_ospf_ext; +extern unsigned long term_debug_ospf_sr; +extern unsigned long term_debug_ospf_ti_lfa; +extern unsigned long term_debug_ospf_defaultinfo; +extern unsigned long term_debug_ospf_ldp_sync; +extern unsigned long term_debug_ospf_gr; +extern unsigned long term_debug_ospf_bfd; +extern unsigned long term_debug_ospf_client_api; + +/* Message Strings. */ +extern char *ospf_lsa_type_str[]; + +/* Prototypes. */ +extern const char *ospf_area_name_string(struct ospf_area *area); +extern const char *ospf_area_desc_string(struct ospf_area *area); +extern const char *ospf_if_name_string(struct ospf_interface *oip); +extern int ospf_nbr_ism_state(struct ospf_neighbor *nbr); +extern void ospf_nbr_ism_state_message(struct ospf_neighbor *nbr, char *buf, + size_t size); +extern const char *ospf_timer_dump(struct event *e, char *buf, size_t size); +extern const char *ospf_timeval_dump(struct timeval *t, char *buf, size_t size); +extern void ospf_packet_dump(struct stream *s); +extern void ospf_debug_init(void); + +/* Appropriate buffer size to use with ospf_timer_dump and ospf_timeval_dump: */ +#define OSPF_TIME_DUMP_SIZE 16 + +#endif /* _ZEBRA_OSPF_DUMP_H */ diff --git a/ospfd/ospf_dump_api.c b/ospfd/ospf_dump_api.c new file mode 100644 index 0000000..8400310 --- /dev/null +++ b/ospfd/ospf_dump_api.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPFd dump routine (parts used by ospfclient). + * Copyright (C) 1999, 2000 Toshiaki Takada + */ + +#include + +#include "log.h" +#include "prefix.h" + +#include "ospf_dump_api.h" +#include "ospfd.h" +#include "ospf_asbr.h" +#include "ospf_lsa.h" +#include "ospf_nsm.h" +#include "ospf_ism.h" + +const struct message ospf_ism_state_msg[] = { + {ISM_DependUpon, "DependUpon"}, + {ISM_Down, "Down"}, + {ISM_Loopback, "Loopback"}, + {ISM_Waiting, "Waiting"}, + {ISM_PointToPoint, "Point-To-Point"}, + {ISM_DROther, "DROther"}, + {ISM_Backup, "Backup"}, + {ISM_DR, "DR"}, + {0}}; + +const struct message ospf_nsm_state_msg[] = {{NSM_DependUpon, "DependUpon"}, + {NSM_Deleted, "Deleted"}, + {NSM_Down, "Down"}, + {NSM_Attempt, "Attempt"}, + {NSM_Init, "Init"}, + {NSM_TwoWay, "2-Way"}, + {NSM_ExStart, "ExStart"}, + {NSM_Exchange, "Exchange"}, + {NSM_Loading, "Loading"}, + {NSM_Full, "Full"}, + {0}}; + +const struct message ospf_lsa_type_msg[] = { + {OSPF_UNKNOWN_LSA, "unknown"}, + {OSPF_ROUTER_LSA, "router-LSA"}, + {OSPF_NETWORK_LSA, "network-LSA"}, + {OSPF_SUMMARY_LSA, "summary-LSA"}, + {OSPF_ASBR_SUMMARY_LSA, "summary-LSA"}, + {OSPF_AS_EXTERNAL_LSA, "AS-external-LSA"}, + {OSPF_GROUP_MEMBER_LSA, "GROUP MEMBER LSA"}, + {OSPF_AS_NSSA_LSA, "NSSA-LSA"}, + {8, "Type-8 LSA"}, + {OSPF_OPAQUE_LINK_LSA, "Link-Local Opaque-LSA"}, + {OSPF_OPAQUE_AREA_LSA, "Area-Local Opaque-LSA"}, + {OSPF_OPAQUE_AS_LSA, "AS-external Opaque-LSA"}, + {0}}; + +const struct message ospf_link_state_id_type_msg[] = { + {OSPF_UNKNOWN_LSA, "(unknown)"}, + {OSPF_ROUTER_LSA, ""}, + {OSPF_NETWORK_LSA, "(address of Designated Router)"}, + {OSPF_SUMMARY_LSA, "(summary Network Number)"}, + {OSPF_ASBR_SUMMARY_LSA, "(AS Boundary Router address)"}, + {OSPF_AS_EXTERNAL_LSA, "(External Network Number)"}, + {OSPF_GROUP_MEMBER_LSA, "(Group membership information)"}, + {OSPF_AS_NSSA_LSA, "(External Network Number for NSSA)"}, + {8, "(Type-8 LSID)"}, + {OSPF_OPAQUE_LINK_LSA, "(Link-Local Opaque-Type/ID)"}, + {OSPF_OPAQUE_AREA_LSA, "(Area-Local Opaque-Type/ID)"}, + {OSPF_OPAQUE_AS_LSA, "(AS-external Opaque-Type/ID)"}, + {0}}; + +const struct message ospf_network_type_msg[] = { + {OSPF_IFTYPE_NONE, "NONE"}, + {OSPF_IFTYPE_POINTOPOINT, "Point-to-Point"}, + {OSPF_IFTYPE_BROADCAST, "Broadcast"}, + {OSPF_IFTYPE_NBMA, "NBMA"}, + {OSPF_IFTYPE_POINTOMULTIPOINT, "Point-to-MultiPoint"}, + {OSPF_IFTYPE_VIRTUALLINK, "Virtual-Link"}, + {0}}; + +/* AuType */ +const struct message ospf_auth_type_str[] = { + {OSPF_AUTH_NULL, "Null"}, + {OSPF_AUTH_SIMPLE, "Simple"}, + {OSPF_AUTH_CRYPTOGRAPHIC, "Cryptographic"}, + {0}}; + +#define OSPF_OPTION_STR_MAXLEN 24 + +char *ospf_options_dump(uint8_t options) +{ + static char buf[OSPF_OPTION_STR_MAXLEN]; + + snprintf(buf, sizeof(buf), "*|%s|%s|%s|%s|%s|%s|%s", + (options & OSPF_OPTION_O) ? "O" : "-", + (options & OSPF_OPTION_DC) ? "DC" : "-", + (options & OSPF_OPTION_EA) ? "EA" : "-", + (options & OSPF_OPTION_NP) ? "N/P" : "-", + (options & OSPF_OPTION_MC) ? "MC" : "-", + (options & OSPF_OPTION_E) ? "E" : "-", + (options & OSPF_OPTION_MT) ? "M/T" : "-"); + + return buf; +} + +void ospf_lsa_header_dump(struct lsa_header *lsah) +{ + const char *lsah_type = lookup_msg(ospf_lsa_type_msg, lsah->type, NULL); + + zlog_debug(" LSA Header"); + zlog_debug(" LS age %d", ntohs(lsah->ls_age)); + zlog_debug(" Options %d (%s)", lsah->options, + ospf_options_dump(lsah->options)); + zlog_debug(" LS type %d (%s)", lsah->type, + (lsah->type ? lsah_type : "unknown type")); + zlog_debug(" Link State ID %pI4", &lsah->id); + zlog_debug(" Advertising Router %pI4", &lsah->adv_router); + zlog_debug(" LS sequence number 0x%lx", + (unsigned long)ntohl(lsah->ls_seqnum)); + zlog_debug(" LS checksum 0x%x", ntohs(lsah->checksum)); + zlog_debug(" length %d", ntohs(lsah->length)); +} diff --git a/ospfd/ospf_dump_api.h b/ospfd/ospf_dump_api.h new file mode 100644 index 0000000..34da113 --- /dev/null +++ b/ospfd/ospf_dump_api.h @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPFd dump routine (parts used by ospfclient). + * Copyright (C) 1999 Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPF_DUMP_API_H +#define _ZEBRA_OSPF_DUMP_API_H + +#include "log.h" +struct lsa_header; + +extern const struct message ospf_ism_state_msg[]; +extern const struct message ospf_nsm_state_msg[]; +extern const struct message ospf_lsa_type_msg[]; +extern const struct message ospf_link_state_id_type_msg[]; +extern const struct message ospf_network_type_msg[]; +extern const struct message ospf_auth_type_str[]; +extern const int ospf_ism_state_msg_max; +extern const int ospf_nsm_state_msg_max; +extern const int ospf_lsa_type_msg_max; +extern const int ospf_link_state_id_type_msg_max; +extern const int ospf_network_type_msg_max; +extern const size_t ospf_auth_type_str_max; + +extern char *ospf_options_dump(uint8_t); +extern void ospf_lsa_header_dump(struct lsa_header *); + +#endif /* _ZEBRA_OSPF_DUMP_API_H */ diff --git a/ospfd/ospf_errors.c b/ospfd/ospf_errors.c new file mode 100644 index 0000000..cf58c6a --- /dev/null +++ b/ospfd/ospf_errors.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF-specific error messages. + * Copyright (C) 2018 Cumulus Networks, Inc. + * Chirag Shah + */ + +#include + +#include "lib/ferr.h" +#include "ospf_errors.h" + +/* clang-format off */ +static struct log_ref ferr_ospf_warn[] = { + { + .code = EC_OSPF_SET_METRIC_PLUS, + .title = "OSPF does not support `set metric +rtt/-rtt`", + .description = "This implementation of OSPF does not currently support `set metric +rtt/-rtt`", + .suggestion = "Do not use this particular set command for an ospf route-map", + }, + { + .code = EC_OSPF_AUTH, + .title = "OSPF has noticed an authentication issue", + .description = "Something has gone wrong with the calculation of the authentication data", + .suggestion = "Ensure your key is correct, gather log data from this side as well as peer and open an Issue", + }, + { + .code = EC_OSPF_PACKET, + .title = "OSPF has detected packet information mismatch", + .description = "OSPF has detected that packet information received is incorrect", + .suggestion = "Ensure interface configuration is correct, gather log files from here and the peer and open an Issue", + }, + { + .code = EC_OSPF_LARGE_LSA, + .title = "OSPF has determined that a LSA packet will be too large", + .description = "OSPF has received data that is causing it to recalculate how large packets should be and has discovered that the packet size needed would be too large and we will need to fragment packets", + .suggestion = "Further divide up your network with areas", + }, + { + .code = EC_OSPF_LSA_UNEXPECTED, + .title = "OSPF has received a LSA-type that it was not expecting", + .description = "OSPF has received a LSA-type that it was not expecting during processing", + .suggestion = "Gather log data from this machine and it's peer and open an Issue", + }, + { + .code = EC_OSPF_LSA, + .title = "OSPF has discovered inconsistent internal state for a LSA", + .description = "During handling of a LSA, OSPF has discovered that the LSA's internal state is inconsistent", + .suggestion = "Gather log data and open an Issue", + }, + { + .code = EC_OSPF_OPAQUE_REGISTRATION, + .title = "OSPF has failed to properly register Opaque Handler", + .description = "During initialization OSPF has detected a failure to install an opaque handler", + .suggestion = "Gather log data and open an Issue", + }, + { + .code = EC_OSPF_TE_UNEXPECTED, + .title = "OSPF has received TE information that it was not expecting", + .description = "OSPF has received TE information that it was not expecting during normal processing of data", + .suggestion = "Gather log data from this machine and it's peer and open an Issue", + }, + { + .code = EC_OSPF_LSA_INSTALL_FAILURE, + .title = "OSPF was unable to save the LSA into it's database", + .description = "During processing of a new lsa and attempting to save the lsa into the OSPF database, this process failed.", + .suggestion = "Gather log data from this machine and open an Issue", + }, + { + .code = EC_OSPF_LSA_NULL, + .title = "OSPF has received a NULL lsa pointer", + .description = "When processing a LSA update we have found and noticed an internal error where we are passing around a NULL pointer", + .suggestion = "Gather data from this machine and it's peer and open an Issue", + }, + { + .code = EC_OSPF_EXT_LSA_UNEXPECTED, + .title = "OSPF has received EXT information that leaves it in an unexpected state", + .description = "While processing EXT LSA information, OSPF has noticed that the internal state of OSPF has gotten inconsistent", + .suggestion = "Gather data from this machine and it's peer and open an Issue", + }, + { + .code = EC_OSPF_LSA_MISSING, + .title = "OSPF attempted to look up a LSA and it was not found", + .description = "During processing of new LSA information, we attempted to look up an old LSA and it was not found", + .suggestion = "Gather data from this machine and open an Issue", + }, + { + .code = EC_OSPF_PTP_NEIGHBOR, + .title = "OSPF has detected more than 1 neighbor on a P2P interface", + .description = "If you configure a ospf interface as P2P we should not detect more than one neighbor on the interface", + .suggestion = "Fix your config", + }, + { + .code = EC_OSPF_LSA_SIZE, + .title = "OSPF has detected an invalid LSA size", + .description = "OSPF has detected a state where we are attempting to grow a LSA but the LSA has reached it's maximum size", + .suggestion = "Gather data and open an Issue", + }, + { + .code = END_FERR, + } +}; + +static struct log_ref ferr_ospf_err[] = { + { + .code = EC_OSPF_PKT_PROCESS, + .title = "Failure to process a packet", + .description = "OSPF attempted to process a received packet but could not", + .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting" + }, + { + .code = EC_OSPF_ROUTER_LSA_MISMATCH, + .title = "Failure to process Router LSA", + .description = "OSPF attempted to process a Router LSA but Advertising ID mismatch with link id", + .suggestion = "Check OSPF network config for any config issue, If the problem persists, report the problem for troubleshooting" + }, + { + .code = EC_OSPF_DOMAIN_CORRUPT, + .title = "OSPF Domain Corruption", + .description = "OSPF attempted to process a Router LSA but Advertising ID mismatch with link id", + .suggestion = "Check OSPF network Database for corrupted LSA, If the problem persists, shutdown OSPF domain and report the problem for troubleshooting" + }, + { + .code = EC_OSPF_INIT_FAIL, + .title = "OSPF Initialization failure", + .description = "OSPF failed to initialized OSPF default insance", + .suggestion = "Ensure there is adequate memory on the device. If the problem persists, report the problem for troubleshooting" + }, + { + .code = EC_OSPF_SR_INVALID_DB, + .title = "OSPF SR Invalid DB", + .description = "OSPF Segment Routing Database is invalid", + .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting" + }, + { + .code = EC_OSPF_SR_NODE_CREATE, + .title = "OSPF SR hash node creation failed", + .description = "OSPF Segment Routing node creation failed", + .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting" + }, + { + .code = EC_OSPF_SR_INVALID_LSA_ID, + .title = "OSPF SR Invalid LSA ID", + .description = "OSPF Segment Routing invalid lsa id", + .suggestion = "Restart OSPF instance, If the problem persists, report the problem for troubleshooting" + }, + { + .code = EC_OSPF_SR_SID_OVERFLOW, + .title = "OSPF SR Segment-ID overflow", + .description = "OSPF Segment Routing ID index or label exceed Global or Local Block Range", + .suggestion = "Restart OSPF instance, If the problem persists, report the problem for troubleshooting" + }, + { + .code = EC_OSPF_INVALID_ALGORITHM, + .title = "OSPF SR Invalid Algorithm", + .description = "OSPF Segment Routing invalid Algorithm", + .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting" + }, + { + .code = EC_OSPF_FSM_INVALID_STATE, + .title = "OSPF FSM invalid state detected", + .description = "OSPF has attempted to change states when it should not be able to", + .suggestion = "Gather log files and open an issue", + }, + { + .code = EC_OSPF_LARGE_HELLO, + .title = "OSPF Encountered a Large Hello", + .description = "OSPF attempted to send a Hello larger than MTU but did not", + .suggestion = "Too many neighbors configured on a single interface. Suggestion is to decrease the number of neighbors on a single interface/subnet" + }, + { + .code = END_FERR, + } +}; +/* clang-format on */ + +void ospf_error_init(void) +{ + log_ref_add(ferr_ospf_warn); + log_ref_add(ferr_ospf_err); +} diff --git a/ospfd/ospf_errors.h b/ospfd/ospf_errors.h new file mode 100644 index 0000000..e63cb74 --- /dev/null +++ b/ospfd/ospf_errors.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF-specific error messages. + * Copyright (C) 2018 Cumulus Networks, Inc. + * Chirag Shah + */ + +#ifndef __OSPF_ERRORS_H__ +#define __OSPF_ERRORS_H__ + +#include "lib/ferr.h" + +enum ospf_log_refs { + EC_OSPF_PKT_PROCESS = OSPF_FERR_START, + EC_OSPF_ROUTER_LSA_MISMATCH, + EC_OSPF_DOMAIN_CORRUPT, + EC_OSPF_INIT_FAIL, + EC_OSPF_SR_INVALID_DB, + EC_OSPF_SR_NODE_CREATE, + EC_OSPF_SR_INVALID_LSA_ID, + EC_OSPF_SR_SID_OVERFLOW, + EC_OSPF_INVALID_ALGORITHM, + EC_OSPF_FSM_INVALID_STATE, + EC_OSPF_SET_METRIC_PLUS, + EC_OSPF_AUTH, + EC_OSPF_PACKET, + EC_OSPF_LARGE_LSA, + EC_OSPF_LSA_UNEXPECTED, + EC_OSPF_LSA, + EC_OSPF_OPAQUE_REGISTRATION, + EC_OSPF_TE_UNEXPECTED, + EC_OSPF_LSA_INSTALL_FAILURE, + EC_OSPF_LSA_NULL, + EC_OSPF_EXT_LSA_UNEXPECTED, + EC_OSPF_LSA_MISSING, + EC_OSPF_PTP_NEIGHBOR, + EC_OSPF_LSA_SIZE, + EC_OSPF_LARGE_HELLO, +}; + +extern void ospf_error_init(void); + +#endif diff --git a/ospfd/ospf_ext.c b/ospfd/ospf_ext.c new file mode 100644 index 0000000..75b5803 --- /dev/null +++ b/ospfd/ospf_ext.c @@ -0,0 +1,1952 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This is an implementation of RFC7684 OSPFv2 Prefix/Link Attribute + * Advertisement + * + * Module name: Extended Prefix/Link Opaque LSA + * + * Author: Olivier Dugeon + * Author: Anselme Sawadogo + * + * Copyright (C) 2016 - 2018 Orange Labs http://www.orange.com + */ + +#include +#include +#include +#include + +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "memory.h" +#include "command.h" +#include "vty.h" +#include "stream.h" +#include "log.h" +#include "frrevent.h" +#include "hash.h" +#include "sockunion.h" /* for inet_aton() */ +#include "network.h" +#include "if.h" +#include "libospf.h" /* for ospf interface types */ + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_sr.h" +#include "ospfd/ospf_ext.h" +#include "ospfd/ospf_errors.h" + +/* Following structure are internal use only. */ + +/* + * Global variable to manage Extended Prefix/Link Opaque LSA on this node. + * Note that all parameter values are stored in network byte order. + */ +static struct ospf_ext_lp OspfEXT; + +/* + * ----------------------------------------------------------------------- + * Following are initialize/terminate functions for Extended Prefix/Link + * Opaque LSA handling. + * ----------------------------------------------------------------------- + */ + +/* Extended Prefix Opaque LSA related callback functions */ +static void ospf_ext_pref_show_info(struct vty *vty, struct json_object *json, + struct ospf_lsa *lsa); +static int ospf_ext_pref_lsa_originate(void *arg); +static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa); +static void ospf_ext_pref_lsa_schedule(struct ext_itf *exti, + enum lsa_opcode opcode); +/* Extended Link Opaque LSA related callback functions */ +static int ospf_ext_link_new_if(struct interface *ifp); +static int ospf_ext_link_del_if(struct interface *ifp); +static void ospf_ext_ism_change(struct ospf_interface *oi, int old_status); +static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status); +static void ospf_ext_link_show_info(struct vty *vty, struct json_object *json, + struct ospf_lsa *lsa); +static int ospf_ext_link_lsa_originate(void *arg); +static struct ospf_lsa *ospf_ext_link_lsa_refresh(struct ospf_lsa *lsa); +static void ospf_ext_link_lsa_schedule(struct ext_itf *exti, + enum lsa_opcode opcode); +static void ospf_ext_lsa_schedule(struct ext_itf *exti, enum lsa_opcode op); +static int ospf_ext_link_lsa_update(struct ospf_lsa *lsa); +static int ospf_ext_pref_lsa_update(struct ospf_lsa *lsa); +static void ospf_ext_link_delete_adj_sid(struct ext_itf *exti); +static void del_ext_info(void *val); + +/* + * Extended Link/Prefix initialization + * + * @param - none + * + * @return - 0 if OK, <> 0 otherwise + */ +int ospf_ext_init(void) +{ + int rc = 0; + + memset(&OspfEXT, 0, sizeof(OspfEXT)); + OspfEXT.enabled = false; + /* Only Area flooding is supported yet */ + OspfEXT.scope = OSPF_OPAQUE_AREA_LSA; + /* Initialize interface list */ + OspfEXT.iflist = list_new(); + OspfEXT.iflist->del = del_ext_info; + + zlog_info("EXT (%s): Register Extended Link Opaque LSA", __func__); + rc = ospf_register_opaque_functab( + OSPF_OPAQUE_AREA_LSA, OPAQUE_TYPE_EXTENDED_LINK_LSA, + ospf_ext_link_new_if, /* new if */ + ospf_ext_link_del_if, /* del if */ + ospf_ext_ism_change, /* ism change */ + ospf_ext_link_nsm_change, /* nsm change */ + NULL, /* Write router config. */ + NULL, /* Write interface conf. */ + NULL, /* Write debug config. */ + ospf_ext_link_show_info, /* Show LSA info */ + ospf_ext_link_lsa_originate, /* Originate LSA */ + ospf_ext_link_lsa_refresh, /* Refresh LSA */ + ospf_ext_link_lsa_update, /* new_lsa_hook */ + NULL); /* del_lsa_hook */ + + if (rc != 0) { + flog_warn(EC_OSPF_OPAQUE_REGISTRATION, + "EXT (%s): Failed to register Extended Link LSA", + __func__); + return rc; + } + + zlog_info("EXT (%s): Register Extended Prefix Opaque LSA", __func__); + rc = ospf_register_opaque_functab( + OspfEXT.scope, OPAQUE_TYPE_EXTENDED_PREFIX_LSA, + NULL, /* new if handle by link */ + NULL, /* del if handle by link */ + NULL, /* ism change */ + NULL, /* nsm change */ + ospf_sr_config_write_router, /* Write router config. */ + NULL, /* Write interface conf. */ + NULL, /* Write debug config. */ + ospf_ext_pref_show_info, /* Show LSA info */ + ospf_ext_pref_lsa_originate, /* Originate LSA */ + ospf_ext_pref_lsa_refresh, /* Refresh LSA */ + ospf_ext_pref_lsa_update, /* new_lsa_hook */ + NULL); /* del_lsa_hook */ + if (rc != 0) { + flog_warn(EC_OSPF_OPAQUE_REGISTRATION, + "EXT (%s): Failed to register Extended Prefix LSA", + __func__); + return rc; + } + + return rc; +} + +/* + * Extended Link/Prefix termination function + * + * @param - none + * @return - none + */ +void ospf_ext_term(void) +{ + + if ((OspfEXT.scope == OSPF_OPAQUE_AREA_LSA) + || (OspfEXT.scope == OSPF_OPAQUE_AS_LSA)) + ospf_delete_opaque_functab(OspfEXT.scope, + OPAQUE_TYPE_EXTENDED_PREFIX_LSA); + + ospf_delete_opaque_functab(OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_EXTENDED_LINK_LSA); + + list_delete(&OspfEXT.iflist); + OspfEXT.scope = 0; + OspfEXT.enabled = false; + + return; +} + +/* + * Extended Link/Prefix finish function + * + * @param - none + * @return - none + */ +void ospf_ext_finish(void) +{ + + struct listnode *node; + struct ext_itf *exti; + + /* Flush Router Info LSA */ + for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) + ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA); + + OspfEXT.enabled = false; +} + +/* + * --------------------------------------------------------------------- + * Following are control functions for Extended Prefix/Link Opaque LSA + * parameters management. + * --------------------------------------------------------------------- + */ + +/* Functions to free memory space */ +static void del_ext_info(void *val) +{ + XFREE(MTYPE_OSPF_EXT_PARAMS, val); +} + +/* Increment instance value for Extended Prefix Opaque LSAs Opaque ID field */ +static uint32_t get_ext_pref_instance_value(void) +{ + static uint32_t seqno = 0; + + if (seqno < MAX_LEGAL_EXT_INSTANCE_NUM) + seqno += 1; + else + seqno = 1; /* Avoid zero. */ + + return seqno; +} + +/* Increment instance value for Extended Link Opaque LSAs Opaque ID field */ +static uint32_t get_ext_link_instance_value(void) +{ + static uint32_t seqno = 0; + + if (seqno < MAX_LEGAL_EXT_INSTANCE_NUM) + seqno += 1; + else + seqno = 1; /* Avoid zero. */ + + return seqno; +} + +/* Lookup Extended Prefix/Links by ifp from OspfEXT struct iflist */ +static struct ext_itf *lookup_ext_by_ifp(struct interface *ifp) +{ + struct listnode *node; + struct ext_itf *exti; + + for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) + if (exti->ifp == ifp) + return exti; + + return NULL; +} + +/* Lookup Extended Prefix/Links by LSA ID from OspfEXT struct iflist */ +static struct ext_itf *lookup_ext_by_instance(struct ospf_lsa *lsa) +{ + struct listnode *node; + struct ext_itf *exti; + uint32_t key = GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr)); + uint8_t type = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)); + + + for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) + if ((exti->instance == key) && (exti->type == type)) + return exti; + + return NULL; +} + +/* + * ---------------------------------------------------------------------- + * The underlying subsection defines setters and unsetters to create and + * delete tlvs and subtlvs + * ---------------------------------------------------------------------- + */ + +/* Extended Prefix TLV - RFC7684 section 2.1 */ +static void set_ext_prefix(struct ext_itf *exti, uint8_t route_type, + uint8_t flags, struct prefix_ipv4 p) +{ + + TLV_TYPE(exti->prefix) = htons(EXT_TLV_PREFIX); + /* Warning: Size must be adjust depending of subTLV's */ + TLV_LEN(exti->prefix) = htons(EXT_TLV_PREFIX_SIZE); + exti->prefix.route_type = route_type; + exti->prefix.flags = flags; + /* Only Address Family Ipv4 (0) is defined in RFC 7684 */ + exti->prefix.af = 0; + exti->prefix.pref_length = p.prefixlen; + exti->prefix.address = p.prefix; + + SET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE); +} + +/* Extended Link TLV - RFC7684 section 3.1 */ +static void set_ext_link(struct ext_itf *exti, uint8_t type, struct in_addr id, + struct in_addr data) +{ + + TLV_TYPE(exti->link) = htons(EXT_TLV_LINK); + /* Warning: Size must be adjust depending of subTLV's */ + TLV_LEN(exti->link) = htons(EXT_TLV_LINK_SIZE); + exti->link.link_type = type; + exti->link.link_id = id; + exti->link.link_data = data; +} + +/* Prefix SID SubTLV - section 5 */ +static void set_prefix_sid(struct ext_itf *exti, uint8_t algorithm, + uint32_t value, int value_type, uint8_t flags) +{ + + if ((algorithm != SR_ALGORITHM_SPF) + && (algorithm != SR_ALGORITHM_STRICT_SPF)) { + flog_err(EC_OSPF_INVALID_ALGORITHM, + "EXT (%s): unrecognized algorithm, not SPF or S-SPF", + __func__); + return; + } + + /* Update flags according to the type of value field: label or index */ + if (value_type == SID_LABEL) + SET_FLAG(flags, EXT_SUBTLV_PREFIX_SID_VFLG); + + /* set prefix sid subtlv for an extended prefix tlv */ + TLV_TYPE(exti->node_sid) = htons(EXT_SUBTLV_PREFIX_SID); + exti->node_sid.algorithm = algorithm; + exti->node_sid.flags = flags; + exti->node_sid.mtid = 0; /* Multi-Topology is not supported */ + + /* Set Label or Index value */ + if (value_type == SID_LABEL) { + TLV_LEN(exti->node_sid) = + htons(SID_LABEL_SIZE(EXT_SUBTLV_PREFIX_SID_SIZE)); + exti->node_sid.value = htonl(SET_LABEL(value)); + } else { + TLV_LEN(exti->node_sid) = + htons(SID_INDEX_SIZE(EXT_SUBTLV_PREFIX_SID_SIZE)); + exti->node_sid.value = htonl(value); + } +} + +/* Adjacency SID SubTLV - section 6.1 */ +static void set_adj_sid(struct ext_itf *exti, bool backup, uint32_t value, + int value_type) +{ + int index; + uint8_t flags; + + /* Determine which ADJ_SID must be set: nominal or backup */ + if (backup) { + flags = EXT_SUBTLV_LINK_ADJ_SID_BFLG; + index = 1; + } else { + index = 0; + flags = 0; + } + + /* Set Header */ + TLV_TYPE(exti->adj_sid[index]) = htons(EXT_SUBTLV_ADJ_SID); + + /* Only Local ADJ-SID is supported for the moment */ + SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_LFLG); + + exti->adj_sid[index].mtid = 0; /* Multi-Topology is not supported */ + + /* Adjust Length, Flags and Value depending on the type of Label */ + if (value_type == SID_LABEL) { + SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG); + TLV_LEN(exti->adj_sid[index]) = + htons(SID_LABEL_SIZE(EXT_SUBTLV_ADJ_SID_SIZE)); + exti->adj_sid[index].value = htonl(SET_LABEL(value)); + } else { + UNSET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG); + TLV_LEN(exti->adj_sid[index]) = + htons(SID_INDEX_SIZE(EXT_SUBTLV_ADJ_SID_SIZE)); + exti->adj_sid[index].value = htonl(value); + } + + exti->adj_sid[index].flags = flags; /* Set computed flags */ + exti->adj_sid[index].mtid = 0; /* Multi-Topology is not supported */ + exti->adj_sid[index].weight = 0; /* Load-Balancing is not supported */ +} + +/* LAN Adjacency SID SubTLV - section 6.2 */ +static void set_lan_adj_sid(struct ext_itf *exti, bool backup, uint32_t value, + int value_type, struct in_addr neighbor_id) +{ + + int index; + uint8_t flags; + + /* Determine which ADJ_SID must be set: nominal or backup */ + if (backup) { + flags = EXT_SUBTLV_LINK_ADJ_SID_BFLG; + index = 1; + } else { + index = 0; + flags = 0; + } + + /* Set Header */ + TLV_TYPE(exti->lan_sid[index]) = htons(EXT_SUBTLV_LAN_ADJ_SID); + + /* Only Local ADJ-SID is supported for the moment */ + SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_LFLG); + + /* Adjust Length, Flags and Value depending on the type of Label */ + if (value_type == SID_LABEL) { + SET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG); + TLV_LEN(exti->lan_sid[index]) = + htons(SID_LABEL_SIZE(EXT_SUBTLV_PREFIX_RANGE_SIZE)); + exti->lan_sid[index].value = htonl(SET_LABEL(value)); + } else { + UNSET_FLAG(flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG); + TLV_LEN(exti->lan_sid[index]) = + htons(SID_INDEX_SIZE(EXT_SUBTLV_PREFIX_RANGE_SIZE)); + exti->lan_sid[index].value = htonl(value); + } + + exti->lan_sid[index].flags = flags; /* Set computed flags */ + exti->lan_sid[index].mtid = 0; /* Multi-Topology is not supported */ + exti->lan_sid[index].weight = 0; /* Load-Balancing is not supported */ + exti->lan_sid[index].neighbor_id = neighbor_id; +} + +static void unset_adjacency_sid(struct ext_itf *exti) +{ + /* Reset Adjacency TLV */ + if (exti->type == ADJ_SID) { + TLV_TYPE(exti->adj_sid[0]) = 0; + TLV_TYPE(exti->adj_sid[1]) = 0; + } + /* or Lan-Adjacency TLV */ + if (exti->type == LAN_ADJ_SID) { + TLV_TYPE(exti->lan_sid[0]) = 0; + TLV_TYPE(exti->lan_sid[1]) = 0; + } +} + +/* Experimental SubTLV from Cisco */ +static void set_rmt_itf_addr(struct ext_itf *exti, struct in_addr rmtif) +{ + + TLV_TYPE(exti->rmt_itf_addr) = htons(EXT_SUBTLV_RMT_ITF_ADDR); + TLV_LEN(exti->rmt_itf_addr) = htons(sizeof(struct in_addr)); + exti->rmt_itf_addr.value = rmtif; +} + +/* Delete Extended LSA */ +static void ospf_extended_lsa_delete(struct ext_itf *exti) +{ + + /* Avoid deleting LSA if Extended is not enable */ + if (!OspfEXT.enabled) + return; + + /* Process only Active Extended Prefix/Link LSA */ + if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) + return; + + osr_debug("EXT (%s): Disable %s%s%s-SID on interface %s", __func__, + exti->stype == LOCAL_SID ? "Prefix" : "", + exti->stype == ADJ_SID ? "Adjacency" : "", + exti->stype == LAN_ADJ_SID ? "LAN-Adjacency" : "", + exti->ifp->name); + + /* Flush LSA if already engaged */ + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) { + ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA); + UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED); + } + + /* De-activate this Extended Prefix/Link and remove corresponding + * Segment-Routing Prefix-SID or (LAN)-ADJ-SID */ + if (exti->stype == ADJ_SID || exti->stype == LAN_ADJ_SID) + ospf_ext_link_delete_adj_sid(exti); + else + ospf_sr_ext_itf_delete(exti); +} + +/* + * Update Extended prefix SID index for Loopback interface type + * + * @param ifname - Loopback interface name + * @param index - new value for the prefix SID of this interface + * @param p - prefix for this interface or NULL if Extended Prefix + * should be remove + * + * @return instance number if update is OK, 0 otherwise + */ +uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, uint32_t index, + struct prefix_ipv4 *p, uint8_t flags) +{ + int rc = 0; + struct ext_itf *exti; + + /* Find Extended Prefix interface */ + exti = lookup_ext_by_ifp(ifp); + if (exti == NULL) + return rc; + + if (p != NULL) { + osr_debug("EXT (%s): Schedule new prefix %pFX with index %u on interface %s", __func__, p, index, ifp->name); + + /* Set first Extended Prefix then the Prefix SID information */ + set_ext_prefix(exti, OSPF_PATH_INTRA_AREA, EXT_TLV_PREF_NFLG, + *p); + set_prefix_sid(exti, SR_ALGORITHM_SPF, index, SID_INDEX, flags); + + /* Try to Schedule LSA */ + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) { + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) + ospf_ext_pref_lsa_schedule(exti, + REFRESH_THIS_LSA); + else + ospf_ext_pref_lsa_schedule( + exti, REORIGINATE_THIS_LSA); + } + } else { + osr_debug("EXT (%s): Remove prefix for interface %s", __func__, + ifp->name); + + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) + ospf_ext_pref_lsa_schedule(exti, FLUSH_THIS_LSA); + } + + return SET_OPAQUE_LSID(exti->type, exti->instance); +} + +/** + * Update Adjacecny-SID for Extended Link LSA + * + * @param exti Extended Link information + */ +static void ospf_ext_link_update_adj_sid(struct ext_itf *exti) +{ + mpls_label_t label; + mpls_label_t bck_label; + + /* Process only (LAN)Adjacency-SID Type */ + if (exti->stype != ADJ_SID && exti->stype != LAN_ADJ_SID) + return; + + /* Request Primary & Backup Labels from Label Manager */ + bck_label = ospf_sr_local_block_request_label(); + label = ospf_sr_local_block_request_label(); + if (bck_label == MPLS_INVALID_LABEL || label == MPLS_INVALID_LABEL) { + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) + ospf_ext_lsa_schedule(exti, FLUSH_THIS_LSA); + return; + } + + /* Set Adjacency-SID, backup first */ + if (exti->stype == ADJ_SID) { + set_adj_sid(exti, true, bck_label, SID_LABEL); + set_adj_sid(exti, false, label, SID_LABEL); + } else { + set_lan_adj_sid(exti, true, bck_label, SID_LABEL, + exti->lan_sid[0].neighbor_id); + set_lan_adj_sid(exti, false, label, SID_LABEL, + exti->lan_sid[1].neighbor_id); + } + + /* Finally, add corresponding SR Link in SRDB & MPLS LFIB */ + SET_FLAG(exti->flags, EXT_LPFLG_FIB_ENTRY_SET); + ospf_sr_ext_itf_add(exti); +} + +/** + * Delete Adjacecny-SID for Extended Link LSA + * + * @param exti Extended Link information + */ +static void ospf_ext_link_delete_adj_sid(struct ext_itf *exti) +{ + /* Process only (LAN)Adjacency-SID Type */ + if (exti->stype != ADJ_SID && exti->stype != LAN_ADJ_SID) + return; + + /* Release Primary & Backup Labels from Label Manager */ + if (exti->stype == ADJ_SID) { + ospf_sr_local_block_release_label(exti->adj_sid[0].value); + ospf_sr_local_block_release_label(exti->adj_sid[1].value); + } else { + ospf_sr_local_block_release_label(exti->lan_sid[0].value); + ospf_sr_local_block_release_label(exti->lan_sid[1].value); + } + /* And reset corresponding TLV */ + unset_adjacency_sid(exti); + + /* Finally, remove corresponding SR Link in SRDB & MPLS LFIB */ + UNSET_FLAG(exti->flags, EXT_LPFLG_FIB_ENTRY_SET); + ospf_sr_ext_itf_delete(exti); +} + +/** + * Update Extended Link LSA once Segment Routing Label Block has been changed. + */ +void ospf_ext_link_srlb_update(void) +{ + struct listnode *node; + struct ext_itf *exti; + + + osr_debug("EXT (%s): Update Extended Links with new SRLB", __func__); + + /* Update all Extended Link Adjaceny-SID */ + for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) { + /* Skip Extended Prefix */ + if (exti->stype == PREF_SID || exti->stype == LOCAL_SID) + continue; + + /* Skip inactive Extended Link */ + if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) + continue; + + ospf_ext_link_update_adj_sid(exti); + } +} + +/* + * Used by Segment Routing to activate/deactivate Extended Link/Prefix flooding + * + * @param enable To activate or not Segment Routing Extended LSA flooding + * + * @return none + */ +void ospf_ext_update_sr(bool enable) +{ + struct listnode *node; + struct ext_itf *exti; + + osr_debug("EXT (%s): %s Extended LSAs for Segment Routing ", __func__, + enable ? "Enable" : "Disable"); + + if (enable) { + OspfEXT.enabled = true; + + /* Refresh LSAs if already engaged or originate */ + for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) { + /* Skip Inactive Extended Link */ + if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) + continue; + + /* Update Extended Link (LAN)Adj-SID if not set */ + if (!CHECK_FLAG(exti->flags, EXT_LPFLG_FIB_ENTRY_SET)) + ospf_ext_link_update_adj_sid(exti); + + /* Finally, flood the extended Link */ + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) + ospf_ext_lsa_schedule(exti, REFRESH_THIS_LSA); + else + ospf_ext_lsa_schedule(exti, + REORIGINATE_THIS_LSA); + } + } else { + /* Start by Removing Extended LSA */ + for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) + ospf_extended_lsa_delete(exti); + + /* And then disable Extended Link/Prefix */ + OspfEXT.enabled = false; + } +} + +/* + * ----------------------------------------------------------------------- + * Following are callback functions against generic Opaque-LSAs handling + * ----------------------------------------------------------------------- + */ + +/* Add new Interface in Extended Interface List */ +static int ospf_ext_link_new_if(struct interface *ifp) +{ + struct ext_itf *new; + int rc = -1; + + if (lookup_ext_by_ifp(ifp) != NULL) { + rc = 0; /* Do nothing here. */ + return rc; + } + + new = XCALLOC(MTYPE_OSPF_EXT_PARAMS, sizeof(struct ext_itf)); + + /* initialize new information and link back the interface */ + new->ifp = ifp; + new->flags = EXT_LPFLG_LSA_INACTIVE; + + listnode_add(OspfEXT.iflist, new); + + rc = 0; + return rc; +} + +/* Remove existing Interface from Extended Interface List */ +static int ospf_ext_link_del_if(struct interface *ifp) +{ + struct ext_itf *exti; + int rc = -1; + + exti = lookup_ext_by_ifp(ifp); + if (exti != NULL) { + /* Flush LSA and remove Adjacency SID */ + ospf_extended_lsa_delete(exti); + + /* Dequeue listnode entry from the list. */ + listnode_delete(OspfEXT.iflist, exti); + + XFREE(MTYPE_OSPF_EXT_PARAMS, exti); + + rc = 0; + } else { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): interface %s is not found", __func__, + ifp ? ifp->name : "-"); + } + + return rc; +} + +/* + * Determine if an Interface belongs to an Extended Link Adjacency or + * Extended Prefix SID type and allocate new instance value accordingly + */ +static void ospf_ext_ism_change(struct ospf_interface *oi, int old_status) +{ + struct ext_itf *exti; + + /* Get interface information for Segment Routing */ + exti = lookup_ext_by_ifp(oi->ifp); + if (exti == NULL) { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): Cannot get Extended info. from OI(%s)", + __func__, IF_NAME(oi)); + return; + } + + /* Reset Extended information if ospf interface goes Down */ + if (oi->state == ISM_Down) { + ospf_extended_lsa_delete(exti); + exti->area = NULL; + exti->flags = EXT_LPFLG_LSA_INACTIVE; + return; + } + + /* Determine if interface is related to a Prefix or an Adjacency SID */ + if (oi->type == OSPF_IFTYPE_LOOPBACK) { + exti->stype = PREF_SID; + exti->type = OPAQUE_TYPE_EXTENDED_PREFIX_LSA; + exti->instance = get_ext_pref_instance_value(); + exti->area = oi->area; + + /* Complete SRDB if the interface belongs to a Prefix */ + if (OspfEXT.enabled) { + osr_debug("EXT (%s): Set Prefix SID to interface %s ", + __func__, oi->ifp->name); + ospf_sr_update_local_prefix(oi->ifp, oi->address); + } + } else { + /* Determine if interface is related to Adj. or LAN Adj. SID */ + if (oi->state == ISM_DR) + exti->stype = LAN_ADJ_SID; + else + exti->stype = ADJ_SID; + + exti->type = OPAQUE_TYPE_EXTENDED_LINK_LSA; + exti->instance = get_ext_link_instance_value(); + exti->area = oi->area; + + /* + * Note: Adjacency SID information are completed when ospf + * adjacency become up see ospf_ext_link_nsm_change() + */ + if (OspfEXT.enabled) + osr_debug( + "EXT (%s): Set %sAdjacency SID for interface %s ", + __func__, exti->stype == ADJ_SID ? "" : "LAN-", + oi->ifp->name); + } +} + +/* + * Finish Extended Link configuration and flood corresponding LSA + * when OSPF adjacency on this link fire up + */ +static void ospf_ext_link_nsm_change(struct ospf_neighbor *nbr, int old_status) +{ + struct ospf_interface *oi = nbr->oi; + struct ext_itf *exti; + + /* Process Link only when neighbor old or new state is NSM Full */ + if (nbr->state != NSM_Full && old_status != NSM_Full) + return; + + /* Get interface information for Segment Routing */ + exti = lookup_ext_by_ifp(oi->ifp); + if (exti == NULL) { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): Cannot get Extended info. from OI(%s)", + __func__, IF_NAME(oi)); + return; + } + + /* Check that we have a valid area and ospf context */ + if (oi->area == NULL || oi->area->ospf == NULL) { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): Cannot refer to OSPF from OI(%s)", + __func__, IF_NAME(oi)); + return; + } + + /* Remove Extended Link if Neighbor State goes Down or Deleted */ + if (OspfEXT.enabled + && (nbr->state == NSM_Down || nbr->state == NSM_Deleted)) { + ospf_ext_link_delete_adj_sid(exti); + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) + ospf_ext_link_lsa_schedule(exti, FLUSH_THIS_LSA); + exti->flags = EXT_LPFLG_LSA_INACTIVE; + return; + } + + /* Keep Area information in combination with SR info. */ + exti->area = oi->area; + + /* Process only Adjacency/LAN SID */ + if (exti->stype == PREF_SID) + return; + + switch (oi->state) { + case ISM_PointToPoint: + /* Segment ID is an Adjacency one */ + exti->stype = ADJ_SID; + + /* Set Extended Link TLV with link_id == Nbr Router ID */ + set_ext_link(exti, OSPF_IFTYPE_POINTOPOINT, nbr->router_id, + oi->address->u.prefix4); + + /* And Remote Interface address */ + set_rmt_itf_addr(exti, nbr->address.u.prefix4); + + break; + + case ISM_DR: + /* Segment ID is a LAN Adjacency for the DR only */ + exti->stype = LAN_ADJ_SID; + + /* Set Extended Link TLV with link_id == DR */ + set_ext_link(exti, OSPF_IFTYPE_BROADCAST, DR(oi), + oi->address->u.prefix4); + + /* Set Neighbor ID */ + exti->lan_sid[0].neighbor_id = nbr->router_id; + exti->lan_sid[1].neighbor_id = nbr->router_id; + + break; + + case ISM_DROther: + case ISM_Backup: + /* Segment ID is an Adjacency if not the DR */ + exti->stype = ADJ_SID; + + /* Set Extended Link TLV with link_id == DR */ + set_ext_link(exti, OSPF_IFTYPE_BROADCAST, DR(oi), + oi->address->u.prefix4); + + break; + + default: + if (CHECK_FLAG(exti->flags, EXT_LPFLG_FIB_ENTRY_SET)) + ospf_ext_link_delete_adj_sid(exti); + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) + ospf_ext_link_lsa_schedule(exti, FLUSH_THIS_LSA); + exti->flags = EXT_LPFLG_LSA_INACTIVE; + return; + } + + SET_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE); + + if (OspfEXT.enabled) { + osr_debug("EXT (%s): Set %sAdjacency SID for interface %s ", + __func__, exti->stype == ADJ_SID ? "" : "LAN-", + oi->ifp->name); + + /* Update (LAN)Adjacency SID */ + ospf_ext_link_update_adj_sid(exti); + + /* flood this links params if everything is ok */ + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) + ospf_ext_link_lsa_schedule(exti, REFRESH_THIS_LSA); + else + ospf_ext_link_lsa_schedule(exti, REORIGINATE_THIS_LSA); + } +} + +/* Callbacks to handle Extended Link Segment Routing LSA information */ +static int ospf_ext_link_lsa_update(struct ospf_lsa *lsa) +{ + /* Sanity Check */ + if (lsa == NULL) { + flog_warn(EC_OSPF_LSA_NULL, "EXT (%s): Abort! LSA is NULL", + __func__); + return -1; + } + + /* Process only Opaque LSA */ + if ((lsa->data->type != OSPF_OPAQUE_AREA_LSA) + && (lsa->data->type != OSPF_OPAQUE_AS_LSA)) + return 0; + + /* Process only Extended Link LSA */ + if (GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)) + != OPAQUE_TYPE_EXTENDED_LINK_LSA) + return 0; + + /* Check if it is not my LSA */ + if (IS_LSA_SELF(lsa)) + return 0; + + /* Check if Extended is enable */ + if (!OspfEXT.enabled) + return 0; + + /* Call Segment Routing LSA update or deletion */ + if (!IS_LSA_MAXAGE(lsa)) + ospf_sr_ext_link_lsa_update(lsa); + else + ospf_sr_ext_link_lsa_delete(lsa); + + return 0; +} + +/* Callbacks to handle Extended Prefix Segment Routing LSA information */ +static int ospf_ext_pref_lsa_update(struct ospf_lsa *lsa) +{ + + /* Sanity Check */ + if (lsa == NULL) { + flog_warn(EC_OSPF_LSA_NULL, "EXT (%s): Abort! LSA is NULL", + __func__); + return -1; + } + + /* Process only Opaque LSA */ + if ((lsa->data->type != OSPF_OPAQUE_AREA_LSA) + && (lsa->data->type != OSPF_OPAQUE_AS_LSA)) + return 0; + + /* Process only Extended Prefix LSA */ + if (GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)) + != OPAQUE_TYPE_EXTENDED_PREFIX_LSA) + return 0; + + /* Check if it is not my LSA */ + if (IS_LSA_SELF(lsa)) + return 0; + + /* Check if Extended is enable */ + if (!OspfEXT.enabled) + return 0; + + /* Call Segment Routing LSA update or deletion */ + if (!IS_LSA_MAXAGE(lsa)) + ospf_sr_ext_prefix_lsa_update(lsa); + else + ospf_sr_ext_prefix_lsa_delete(lsa); + + return 0; +} + +/* + * ------------------------------------------------------- + * Following are OSPF protocol processing functions for + * Extended Prefix/Link Opaque LSA + * ------------------------------------------------------- + */ + +static void build_tlv_header(struct stream *s, struct tlv_header *tlvh) +{ + stream_put(s, tlvh, sizeof(struct tlv_header)); +} + +static void build_tlv(struct stream *s, struct tlv_header *tlvh) +{ + + if ((tlvh != NULL) && (ntohs(tlvh->type) != 0)) { + build_tlv_header(s, tlvh); + stream_put(s, TLV_DATA(tlvh), TLV_BODY_SIZE(tlvh)); + } +} + +/* Build an Extended Prefix Opaque LSA body for extended prefix TLV */ +static void ospf_ext_pref_lsa_body_set(struct stream *s, struct ext_itf *exti) +{ + + /* Sanity check */ + if ((exti == NULL) || (exti->stype != PREF_SID)) + return; + + /* Adjust Extended Prefix TLV size */ + TLV_LEN(exti->prefix) = htons(ntohs(TLV_LEN(exti->node_sid)) + + EXT_TLV_PREFIX_SIZE + TLV_HDR_SIZE); + + /* Build LSA body for an Extended Prefix TLV */ + build_tlv_header(s, &exti->prefix.header); + stream_put(s, TLV_DATA(&exti->prefix.header), EXT_TLV_PREFIX_SIZE); + /* Then add Prefix SID SubTLV */ + build_tlv(s, &exti->node_sid.header); +} + +/* Build an Extended Link Opaque LSA body for extended link TLV */ +static void ospf_ext_link_lsa_body_set(struct stream *s, struct ext_itf *exti) +{ + size_t size; + + /* Sanity check */ + if ((exti == NULL) + || ((exti->stype != ADJ_SID) && (exti->stype != LAN_ADJ_SID))) + return; + + if (exti->stype == ADJ_SID) { + /* Adjust Extended Link TLV size for Adj. SID */ + size = EXT_TLV_LINK_SIZE + 2 * EXT_SUBTLV_ADJ_SID_SIZE + + 2 * TLV_HDR_SIZE; + if (ntohs(TLV_TYPE(exti->rmt_itf_addr)) != 0) + size = size + EXT_SUBTLV_RMT_ITF_ADDR_SIZE + + TLV_HDR_SIZE; + TLV_LEN(exti->link) = htons(size); + + /* Build LSA body for an Extended Link TLV with Adj. SID */ + build_tlv_header(s, &exti->link.header); + stream_put(s, TLV_DATA(&exti->link.header), EXT_TLV_LINK_SIZE); + /* then add Adjacency SubTLVs */ + build_tlv(s, &exti->adj_sid[1].header); + build_tlv(s, &exti->adj_sid[0].header); + + /* Add Cisco experimental SubTLV if interface is PtoP */ + if (ntohs(TLV_TYPE(exti->rmt_itf_addr)) != 0) + build_tlv(s, &exti->rmt_itf_addr.header); + } else { + /* Adjust Extended Link TLV size for LAN SID */ + size = EXT_TLV_LINK_SIZE + + 2 * (EXT_SUBTLV_LAN_ADJ_SID_SIZE + TLV_HDR_SIZE); + TLV_LEN(exti->link) = htons(size); + + /* Build LSA body for an Extended Link TLV with LAN SID */ + build_tlv_header(s, &exti->link.header); + stream_put(s, TLV_DATA(&exti->link.header), EXT_TLV_LINK_SIZE); + /* then add LAN-Adjacency SubTLVs */ + build_tlv(s, &exti->lan_sid[1].header); + build_tlv(s, &exti->lan_sid[0].header); + } +} + +/* Create new Extended Prefix opaque-LSA for every extended prefix */ +static struct ospf_lsa *ospf_ext_pref_lsa_new(struct ospf_area *area, + struct ext_itf *exti) +{ + struct stream *s; + struct lsa_header *lsah; + struct ospf_lsa *new = NULL; + struct ospf *top; + uint8_t options, lsa_type; + struct in_addr lsa_id; + struct in_addr router_id; + uint32_t tmp; + uint16_t length; + + /* Sanity Check */ + if (exti == NULL) + return NULL; + + /* Create a stream for LSA. */ + s = stream_new(OSPF_MAX_LSA_SIZE); + + /* Prepare LSA Header */ + lsah = (struct lsa_header *)STREAM_DATA(s); + + lsa_type = OspfEXT.scope; + + /* + * LSA ID is a variable number identifying different instances of + * Extended Prefix Opaque LSA from the same router see RFC 7684 + */ + tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_PREFIX_LSA, exti->instance); + lsa_id.s_addr = htonl(tmp); + + options = OSPF_OPTION_O; /* Don't forget this :-) */ + + /* Fix Options and Router ID depending of the flooding scope */ + if ((OspfEXT.scope == OSPF_OPAQUE_AS_LSA) || (area == NULL)) { + options = OSPF_OPTION_E; + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + router_id.s_addr = top ? top->router_id.s_addr : 0; + } else { + options |= LSA_OPTIONS_GET(area); /* Get area default option */ + options |= LSA_OPTIONS_NSSA_GET(area); + router_id = area->ospf->router_id; + } + + /* Set opaque-LSA header fields. */ + lsa_header_set(s, options, lsa_type, lsa_id, router_id); + + osr_debug( + "EXT (%s): LSA[Type%u:%pI4]: Create an Opaque-LSA Extended Prefix Opaque LSA instance", + __func__, lsa_type, &lsa_id); + + /* Set opaque-LSA body fields. */ + ospf_ext_pref_lsa_body_set(s, exti); + + /* Set length. */ + length = stream_get_endp(s); + lsah->length = htons(length); + + /* Now, create an OSPF LSA instance. */ + new = ospf_lsa_new_and_data(length); + + /* Segment Routing belongs only to default VRF */ + new->vrf_id = VRF_DEFAULT; + new->area = area; + SET_FLAG(new->flags, OSPF_LSA_SELF); + memcpy(new->data, lsah, length); + stream_free(s); + + return new; +} + +/* Create new Extended Link opaque-LSA for every extended link TLV */ +static struct ospf_lsa *ospf_ext_link_lsa_new(struct ospf_area *area, + struct ext_itf *exti) +{ + struct stream *s; + struct lsa_header *lsah; + struct ospf_lsa *new = NULL; + uint8_t options, lsa_type; + struct in_addr lsa_id; + uint32_t tmp; + uint16_t length; + + /* Sanity Check */ + if (exti == NULL) + return NULL; + + /* Create a stream for LSA. */ + s = stream_new(OSPF_MAX_LSA_SIZE); + lsah = (struct lsa_header *)STREAM_DATA(s); + + options = OSPF_OPTION_O; /* Don't forget this :-) */ + options |= LSA_OPTIONS_GET(area); /* Get area default option */ + options |= LSA_OPTIONS_NSSA_GET(area); + /* Extended Link Opaque LSA are only flooded within an area */ + lsa_type = OSPF_OPAQUE_AREA_LSA; + + /* + * LSA ID is a variable number identifying different instances of + * Extended Link Opaque LSA from the same router see RFC 7684 + */ + tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, exti->instance); + lsa_id.s_addr = htonl(tmp); + + osr_debug( + "EXT (%s) LSA[Type%u:%pI4]: Create an Opaque-LSA Extended Link Opaque LSA instance", + __func__, lsa_type, &lsa_id); + + /* Set opaque-LSA header fields. */ + lsa_header_set(s, options, lsa_type, lsa_id, area->ospf->router_id); + + /* Set opaque-LSA body fields. */ + ospf_ext_link_lsa_body_set(s, exti); + + /* Set length. */ + length = stream_get_endp(s); + lsah->length = htons(length); + + /* Now, create an OSPF LSA instance. */ + new = ospf_lsa_new_and_data(length); + + /* Segment Routing belongs only to default VRF */ + new->vrf_id = VRF_DEFAULT; + new->area = area; + SET_FLAG(new->flags, OSPF_LSA_SELF); + memcpy(new->data, lsah, length); + stream_free(s); + + return new; +} + +/* + * Process the origination of an Extended Prefix Opaque LSA + * for every extended prefix TLV + */ +static int ospf_ext_pref_lsa_originate1(struct ospf_area *area, + struct ext_itf *exti) +{ + struct ospf_lsa *new; + int rc = -1; + + + /* Create new Opaque-LSA/Extended Prefix Opaque LSA instance. */ + new = ospf_ext_pref_lsa_new(area, exti); + if (new == NULL) { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): ospf_ext_pref_lsa_new() error", __func__); + return rc; + } + + /* Install this LSA into LSDB. */ + if (ospf_lsa_install(area->ospf, NULL /*oi */, new) == NULL) { + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "EXT (%s): ospf_lsa_install() error", __func__); + ospf_lsa_unlock(&new); + return rc; + } + + /* Now this Extended Prefix Opaque LSA info parameter entry has + * associated LSA. + */ + SET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED); + + /* Update new LSA origination count. */ + area->ospf->lsa_originate_count++; + + /* Flood new LSA through area. */ + ospf_flood_through_area(area, NULL /*nbr */, new); + + osr_debug( + "EXT (%s): LSA[Type%u:%pI4]: Originate Opaque-LSAExtended Prefix Opaque LSA: Area(%pI4), Link(%s)", + __func__, new->data->type, &new->data->id, + &area->area_id, exti->ifp->name); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + ospf_lsa_header_dump(new->data); + + rc = 0; + + return rc; +} + +/* + * Process the origination of an Extended Link Opaque LSA + * for every extended link TLV + */ +static int ospf_ext_link_lsa_originate1(struct ospf_area *area, + struct ext_itf *exti) +{ + struct ospf_lsa *new; + int rc = -1; + + /* Create new Opaque-LSA/Extended Link Opaque LSA instance. */ + new = ospf_ext_link_lsa_new(area, exti); + if (new == NULL) { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): ospf_ext_link_lsa_new() error", __func__); + return rc; + } + + /* Install this LSA into LSDB. */ + if (ospf_lsa_install(area->ospf, NULL /*oi */, new) == NULL) { + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "EXT (%s): ospf_lsa_install() error", __func__); + ospf_lsa_unlock(&new); + return rc; + } + + /* Now this link-parameter entry has associated LSA. */ + SET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED); + + /* Update new LSA origination count. */ + area->ospf->lsa_originate_count++; + + /* Flood new LSA through area. */ + ospf_flood_through_area(area, NULL /*nbr */, new); + + osr_debug( + "EXT (%s): LSA[Type%u:%pI4]: Originate Opaque-LSA Extended Link Opaque LSA: Area(%pI4), Link(%s)", + __func__, new->data->type, &new->data->id, + &area->area_id, exti->ifp->name); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + ospf_lsa_header_dump(new->data); + + rc = 0; + + return rc; +} + +/* Trigger the origination of Extended Prefix Opaque LSAs */ +static int ospf_ext_pref_lsa_originate(void *arg) +{ + struct ospf_area *area = (struct ospf_area *)arg; + struct listnode *node; + struct ext_itf *exti; + int rc = -1; + + if (!OspfEXT.enabled) { + zlog_info( + "EXT (%s): Segment Routing functionality is Disabled now", + __func__); + rc = 0; /* This is not an error case. */ + return rc; + } + osr_debug("EXT (%s): Start Originate Prefix LSA for area %pI4", + __func__, &area->area_id); + + /* Check if Extended Prefix Opaque LSA is already engaged */ + for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) { + + /* Process only Prefix SID */ + if (exti->stype != PREF_SID) + continue; + + /* Process only Extended Prefix with valid Area ID */ + if ((exti->area == NULL) + || (!IPV4_ADDR_SAME(&exti->area->area_id, &area->area_id))) + continue; + + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) { + if (CHECK_FLAG(exti->flags, + EXT_LPFLG_LSA_FORCED_REFRESH)) { + flog_warn( + EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): Refresh instead of Originate", + __func__); + UNSET_FLAG(exti->flags, + EXT_LPFLG_LSA_FORCED_REFRESH); + ospf_ext_pref_lsa_schedule(exti, + REFRESH_THIS_LSA); + } + continue; + } + + /* Ok, let's try to originate an LSA */ + osr_debug( + "EXT (%s): Let's finally re-originate the LSA 7.0.0.%u for Itf %s", __func__, exti->instance, + exti->ifp ? exti->ifp->name : ""); + ospf_ext_pref_lsa_originate1(area, exti); + } + + rc = 0; + return rc; +} + +/* Trigger the origination of Extended Link Opaque LSAs */ +static int ospf_ext_link_lsa_originate(void *arg) +{ + struct ospf_area *area = (struct ospf_area *)arg; + struct listnode *node; + struct ext_itf *exti; + int rc = -1; + + if (!OspfEXT.enabled) { + zlog_info( + "EXT (%s): Segment Routing functionality is Disabled now", + __func__); + rc = 0; /* This is not an error case. */ + return rc; + } + + /* Check if Extended Prefix Opaque LSA is already engaged */ + for (ALL_LIST_ELEMENTS_RO(OspfEXT.iflist, node, exti)) { + /* Process only Adjacency or LAN SID */ + if (exti->stype == PREF_SID) + continue; + + /* Skip Inactive Extended Link */ + if (!CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) + continue; + + /* Process only Extended Link with valid Area ID */ + if ((exti->area == NULL) + || (!IPV4_ADDR_SAME(&exti->area->area_id, &area->area_id))) + continue; + + /* Check if LSA not already engaged */ + if (CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED)) { + if (CHECK_FLAG(exti->flags, + EXT_LPFLG_LSA_FORCED_REFRESH)) { + flog_warn( + EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): Refresh instead of Originate", + __func__); + UNSET_FLAG(exti->flags, + EXT_LPFLG_LSA_FORCED_REFRESH); + ospf_ext_link_lsa_schedule(exti, + REFRESH_THIS_LSA); + } + continue; + } + + /* Ok, let's try to originate an LSA */ + osr_debug( + "EXT (%s): Let's finally reoriginate the LSA 8.0.0.%u for Itf %s through the Area %pI4", __func__, + exti->instance, exti->ifp ? exti->ifp->name : "-", + &area->area_id); + ospf_ext_link_lsa_originate1(area, exti); + } + + rc = 0; + return rc; +} + +/* Refresh an Extended Prefix Opaque LSA */ +static struct ospf_lsa *ospf_ext_pref_lsa_refresh(struct ospf_lsa *lsa) +{ + struct ospf_lsa *new = NULL; + struct ospf_area *area = lsa->area; + struct ospf *top; + struct ext_itf *exti; + + if (!OspfEXT.enabled) { + /* + * This LSA must have flushed before due to Extended Prefix + * Opaque LSA status change. + * It seems a slip among routers in the routing domain. + */ + zlog_info( + "EXT (%s): Segment Routing functionality is Disabled", + __func__); + /* Flush it anyway. */ + lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + } + + /* Lookup this lsa corresponding Extended parameters */ + exti = lookup_ext_by_instance(lsa); + if (exti == NULL) { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): Invalid parameter LSA ID", __func__); + /* Flush it anyway. */ + lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + } + + /* Check if Interface was not disable in the interval */ + if ((exti != NULL) && !CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): Interface was Disabled: Flush it!", + __func__); + /* Flush it anyway. */ + lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + } + + /* If the lsa's age reached to MaxAge, start flushing procedure. */ + if (IS_LSA_MAXAGE(lsa)) { + if (exti) + UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED); + ospf_opaque_lsa_flush_schedule(lsa); + return NULL; + } + + /* Create new Opaque-LSA/Extended Prefix Opaque LSA instance. */ + new = ospf_ext_pref_lsa_new(area, exti); + + if (new == NULL) { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): ospf_ext_pref_lsa_new() error", __func__); + return NULL; + } + new->data->ls_seqnum = lsa_seqnum_increment(lsa); + + /* + * Install this LSA into LSDB + * Given "lsa" will be freed in the next function + * As area could be NULL i.e. when using OPAQUE_LSA_AS, we prefer to use + * ospf_lookup() to get ospf instance + */ + if (area) + top = area->ospf; + else + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) { + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "EXT (%s): ospf_lsa_install() error", __func__); + ospf_lsa_unlock(&new); + return NULL; + } + + /* Flood updated LSA through the Prefix Area according to the RFC7684 */ + ospf_flood_through_area(area, NULL /*nbr */, new); + + /* Debug logging. */ + osr_debug("EXT (%s): LSA[Type%u:%pI4] Refresh Extended Prefix LSA", + __func__, new->data->type, &new->data->id); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + ospf_lsa_header_dump(new->data); + + + return new; +} + +/* Refresh an Extended Link Opaque LSA */ +static struct ospf_lsa *ospf_ext_link_lsa_refresh(struct ospf_lsa *lsa) +{ + struct ext_itf *exti; + struct ospf_area *area = lsa->area; + struct ospf *top = area->ospf; + struct ospf_lsa *new = NULL; + + if (!OspfEXT.enabled) { + /* + * This LSA must have flushed before due to OSPF-SR status + * change. It seems a slip among routers in the routing domain. + */ + zlog_info("EXT (%s): Segment Routing functionality is Disabled", + __func__); + /* Flush it anyway. */ + lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + } + + /* Lookup this LSA corresponding Extended parameters */ + exti = lookup_ext_by_instance(lsa); + if (exti == NULL) { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): Invalid parameter LSA ID", __func__); + /* Flush it anyway. */ + lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + } + + /* Check if Interface was not disable in the interval */ + if ((exti != NULL) && !CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE)) { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): Interface was Disabled: Flush it!", + __func__); + lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + } + + /* If the lsa's age reached to MaxAge, start flushing procedure */ + if (IS_LSA_MAXAGE(lsa)) { + if (exti) + UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED); + ospf_opaque_lsa_flush_schedule(lsa); + return NULL; + } + + /* Create new Opaque-LSA/Extended Link instance */ + new = ospf_ext_link_lsa_new(area, exti); + if (new == NULL) { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): Error creating new LSA", __func__); + return NULL; + } + new->data->ls_seqnum = lsa_seqnum_increment(lsa); + + /* Install this LSA into LSDB. */ + /* Given "lsa" will be freed in the next function */ + if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) { + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "EXT (%s): Error installing new LSA", __func__); + ospf_lsa_unlock(&new); + return NULL; + } + + /* Flood updated LSA through the link Area according to the RFC7684 */ + ospf_flood_through_area(area, NULL /*nbr */, new); + + /* Debug logging. */ + osr_debug("EXT (%s): LSA[Type%u:%pI4]: Refresh Extended Link LSA", + __func__, new->data->type, &new->data->id); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + ospf_lsa_header_dump(new->data); + + return new; +} + +/* Schedule Extended Prefix Opaque LSA origination/refreshment/flushing */ +static void ospf_ext_pref_lsa_schedule(struct ext_itf *exti, + enum lsa_opcode opcode) +{ + struct ospf_lsa lsa; + struct lsa_header lsah; + struct ospf *top; + uint32_t tmp; + + memset(&lsa, 0, sizeof(lsa)); + memset(&lsah, 0, sizeof(lsah)); + + /* Sanity Check */ + if (exti == NULL) + return; + + /* Check if the corresponding link is ready to be flooded */ + if (!(CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE))) + return; + + osr_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__, + opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "", + opcode == REFRESH_THIS_LSA ? "Refresh" : "", + opcode == FLUSH_THIS_LSA ? "Flush" : "", + exti->ifp ? exti->ifp->name : "-"); + + /* Verify Area */ + if (exti->area == NULL) { + osr_debug( + "EXT (%s): Area is not yet set. Try to use Backbone Area", + __func__); + + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + struct in_addr backbone = {.s_addr = INADDR_ANY}; + exti->area = ospf_area_lookup_by_area_id(top, backbone); + if (exti->area == NULL) { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): Unable to set Area", __func__); + return; + } + } + /* Set LSA header information */ + lsa.area = exti->area; + lsa.data = &lsah; + lsah.type = OSPF_OPAQUE_AREA_LSA; + tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_PREFIX_LSA, exti->instance); + lsah.id.s_addr = htonl(tmp); + + switch (opcode) { + case REORIGINATE_THIS_LSA: + ospf_opaque_lsa_reoriginate_schedule( + (void *)exti->area, OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_EXTENDED_PREFIX_LSA); + break; + case REFRESH_THIS_LSA: + ospf_opaque_lsa_refresh_schedule(&lsa); + break; + case FLUSH_THIS_LSA: + UNSET_FLAG(exti->flags, EXT_LPFLG_LSA_ENGAGED); + ospf_opaque_lsa_flush_schedule(&lsa); + break; + } +} + +/* Schedule Extended Link Opaque LSA origination/refreshment/flushing */ +static void ospf_ext_link_lsa_schedule(struct ext_itf *exti, + enum lsa_opcode opcode) +{ + struct ospf_lsa lsa; + struct lsa_header lsah; + struct ospf *top; + uint32_t tmp; + + memset(&lsa, 0, sizeof(lsa)); + memset(&lsah, 0, sizeof(lsah)); + + /* Sanity Check */ + if (exti == NULL) + return; + + /* Check if the corresponding link is ready to be flooded */ + if (!(CHECK_FLAG(exti->flags, EXT_LPFLG_LSA_ACTIVE))) + return; + + osr_debug("EXT (%s): Schedule %s%s%s LSA for interface %s", __func__, + opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "", + opcode == REFRESH_THIS_LSA ? "Refresh" : "", + opcode == FLUSH_THIS_LSA ? "Flush" : "", + exti->ifp ? exti->ifp->name : "-"); + + /* Verify Area */ + if (exti->area == NULL) { + osr_debug( + "EXT (%s): Area is not yet set. Try to use Backbone Area", + __func__); + + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + struct in_addr backbone = {.s_addr = INADDR_ANY}; + exti->area = ospf_area_lookup_by_area_id(top, backbone); + if (exti->area == NULL) { + flog_warn(EC_OSPF_EXT_LSA_UNEXPECTED, + "EXT (%s): Unable to set Area", __func__); + return; + } + } + /* Set LSA header information */ + lsa.area = exti->area; + lsa.data = &lsah; + lsah.type = OSPF_OPAQUE_AREA_LSA; + tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, exti->instance); + lsah.id.s_addr = htonl(tmp); + + switch (opcode) { + case REORIGINATE_THIS_LSA: + ospf_opaque_lsa_reoriginate_schedule( + (void *)exti->area, OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_EXTENDED_LINK_LSA); + break; + case REFRESH_THIS_LSA: + ospf_opaque_lsa_refresh_schedule(&lsa); + break; + case FLUSH_THIS_LSA: + ospf_opaque_lsa_flush_schedule(&lsa); + break; + } +} + +/* Schedule Extended Link or Prefix depending of the Type of LSA */ +static void ospf_ext_lsa_schedule(struct ext_itf *exti, enum lsa_opcode op) +{ + + if (exti->stype == PREF_SID) + ospf_ext_pref_lsa_schedule(exti, op); + else + ospf_ext_link_lsa_schedule(exti, op); +} + +/* + * ------------------------------------ + * Following are vty show functions. + * ------------------------------------ + */ + +#define check_tlv_size(size, msg) \ + do { \ + if (ntohs(tlvh->length) != size) { \ + vty_out(vty, " Wrong %s TLV size: %d(%d). Abort!\n", \ + msg, ntohs(tlvh->length), size); \ + return size + TLV_HDR_SIZE; \ + } \ + } while (0) + +/* Cisco experimental SubTLV */ +static uint16_t show_vty_ext_link_rmt_itf_addr(struct vty *vty, + struct tlv_header *tlvh) +{ + struct ext_subtlv_rmt_itf_addr *top = + (struct ext_subtlv_rmt_itf_addr *)tlvh; + + check_tlv_size(EXT_SUBTLV_RMT_ITF_ADDR_SIZE, "Remote Itf. Address"); + + vty_out(vty, + " Remote Interface Address Sub-TLV: Length %u\n Address: %pI4\n", + ntohs(top->header.length), &top->value); + + return TLV_SIZE(tlvh); +} + +/* Adjacency SID SubTLV */ +static uint16_t show_vty_ext_link_adj_sid(struct vty *vty, + struct tlv_header *tlvh) +{ + struct ext_subtlv_adj_sid *top = (struct ext_subtlv_adj_sid *)tlvh; + + check_tlv_size(EXT_SUBTLV_ADJ_SID_SIZE, "Adjacency SID"); + + vty_out(vty, + " Adj-SID Sub-TLV: Length %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\t%s: %u\n", + ntohs(top->header.length), top->flags, top->mtid, top->weight, + CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label" + : "Index", + CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) + ? GET_LABEL(ntohl(top->value)) + : ntohl(top->value)); + + return TLV_SIZE(tlvh); +} + +/* LAN Adjacency SubTLV */ +static uint16_t show_vty_ext_link_lan_adj_sid(struct vty *vty, + struct tlv_header *tlvh) +{ + struct ext_subtlv_lan_adj_sid *top = + (struct ext_subtlv_lan_adj_sid *)tlvh; + + check_tlv_size(EXT_SUBTLV_LAN_ADJ_SID_SIZE, "Lan-Adjacency SID"); + + vty_out(vty, + " LAN-Adj-SID Sub-TLV: Length %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\tWeight: 0x%x\n\tNeighbor ID: %pI4\n\t%s: %u\n", + ntohs(top->header.length), top->flags, top->mtid, top->weight, + &top->neighbor_id, + CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) ? "Label" + : "Index", + CHECK_FLAG(top->flags, EXT_SUBTLV_LINK_ADJ_SID_VFLG) + ? GET_LABEL(ntohl(top->value)) + : ntohl(top->value)); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_unknown_tlv(struct vty *vty, struct tlv_header *tlvh, + size_t buf_size) +{ + if (TLV_SIZE(tlvh) > buf_size) { + vty_out(vty, " TLV size %d exceeds buffer size. Abort!", + TLV_SIZE(tlvh)); + return buf_size; + } + + vty_out(vty, " Unknown TLV: [type(0x%x), length(0x%x)]\n", + ntohs(tlvh->type), ntohs(tlvh->length)); + + return TLV_SIZE(tlvh); +} + +/* Extended Link Sub TLVs */ +static uint16_t show_vty_link_info(struct vty *vty, struct tlv_header *ext, + size_t buf_size) +{ + struct ext_tlv_link *top = (struct ext_tlv_link *)ext; + struct tlv_header *tlvh; + uint16_t length = ntohs(top->header.length); + uint16_t sum = 0; + + /* Verify that TLV length is valid against remaining buffer size */ + if (length > buf_size) { + vty_out(vty, + " Extended Link TLV size %d exceeds buffer size. Abort!\n", + length); + return buf_size; + } + + vty_out(vty, + " Extended Link TLV: Length %u\n Link Type: 0x%x\n" + " Link ID: %pI4\n", + ntohs(top->header.length), top->link_type, + &top->link_id); + vty_out(vty, " Link data: %pI4\n", &top->link_data); + + /* Skip Extended TLV and parse sub-TLVs */ + length -= EXT_TLV_LINK_SIZE; + tlvh = (struct tlv_header *)((char *)(ext) + TLV_HDR_SIZE + + EXT_TLV_LINK_SIZE); + for (; sum < length && tlvh; tlvh = TLV_HDR_NEXT(tlvh)) { + switch (ntohs(tlvh->type)) { + case EXT_SUBTLV_ADJ_SID: + sum += show_vty_ext_link_adj_sid(vty, tlvh); + break; + case EXT_SUBTLV_LAN_ADJ_SID: + sum += show_vty_ext_link_lan_adj_sid(vty, tlvh); + break; + case EXT_SUBTLV_RMT_ITF_ADDR: + sum += show_vty_ext_link_rmt_itf_addr(vty, tlvh); + break; + default: + sum += show_vty_unknown_tlv(vty, tlvh, length - sum); + break; + } + } + + return sum + sizeof(struct ext_tlv_link); +} + +/* Extended Link TLVs */ +static void ospf_ext_link_show_info(struct vty *vty, struct json_object *json, + struct ospf_lsa *lsa) +{ + struct lsa_header *lsah = lsa->data; + struct tlv_header *tlvh; + uint16_t length = 0, sum = 0; + + if (json) + return; + + /* Initialize TLV browsing */ + length = lsa->size - OSPF_LSA_HEADER_SIZE; + + for (tlvh = TLV_HDR_TOP(lsah); sum < length && tlvh; + tlvh = TLV_HDR_NEXT(tlvh)) { + switch (ntohs(tlvh->type)) { + case EXT_TLV_LINK: + sum += show_vty_link_info(vty, tlvh, length - sum); + break; + default: + sum += show_vty_unknown_tlv(vty, tlvh, length - sum); + break; + } + } +} + +/* Prefix SID SubTLV */ +static uint16_t show_vty_ext_pref_pref_sid(struct vty *vty, + struct tlv_header *tlvh) +{ + struct ext_subtlv_prefix_sid *top = + (struct ext_subtlv_prefix_sid *)tlvh; + + check_tlv_size(EXT_SUBTLV_PREFIX_SID_SIZE, "Prefix SID"); + + vty_out(vty, + " Prefix SID Sub-TLV: Length %u\n\tAlgorithm: %u\n\tFlags: 0x%x\n\tMT-ID:0x%x\n\t%s: %u\n", + ntohs(top->header.length), top->algorithm, top->flags, + top->mtid, + CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) ? "Label" + : "Index", + CHECK_FLAG(top->flags, EXT_SUBTLV_PREFIX_SID_VFLG) + ? GET_LABEL(ntohl(top->value)) + : ntohl(top->value)); + + return TLV_SIZE(tlvh); +} + +/* Extended Prefix SubTLVs */ +static uint16_t show_vty_pref_info(struct vty *vty, struct tlv_header *ext, + size_t buf_size) +{ + struct ext_tlv_prefix *top = (struct ext_tlv_prefix *)ext; + struct tlv_header *tlvh; + uint16_t length = ntohs(top->header.length); + uint16_t sum = 0; + + /* Verify that TLV length is valid against remaining buffer size */ + if (length > buf_size) { + vty_out(vty, + " Extended Link TLV size %d exceeds buffer size. Abort!\n", + length); + return buf_size; + } + + vty_out(vty, + " Extended Prefix TLV: Length %u\n\tRoute Type: %u\n" + "\tAddress Family: 0x%x\n\tFlags: 0x%x\n\tAddress: %pI4/%u\n", + ntohs(top->header.length), top->route_type, top->af, top->flags, + &top->address, top->pref_length); + + /* Skip Extended Prefix TLV and parse sub-TLVs */ + length -= EXT_TLV_PREFIX_SIZE; + tlvh = (struct tlv_header *)((char *)(ext) + TLV_HDR_SIZE + + EXT_TLV_PREFIX_SIZE); + for (; sum < length && tlvh; tlvh = TLV_HDR_NEXT(tlvh)) { + switch (ntohs(tlvh->type)) { + case EXT_SUBTLV_PREFIX_SID: + sum += show_vty_ext_pref_pref_sid(vty, tlvh); + break; + default: + sum += show_vty_unknown_tlv(vty, tlvh, length - sum); + break; + } + } + + return sum + sizeof(struct ext_tlv_prefix); +} + +/* Extended Prefix TLVs */ +static void ospf_ext_pref_show_info(struct vty *vty, struct json_object *json, + struct ospf_lsa *lsa) +{ + struct lsa_header *lsah = lsa->data; + struct tlv_header *tlvh; + uint16_t length = 0, sum = 0; + + if (json) + return; + + /* Initialize TLV browsing */ + length = lsa->size - OSPF_LSA_HEADER_SIZE; + + for (tlvh = TLV_HDR_TOP(lsah); sum < length && tlvh; + tlvh = TLV_HDR_NEXT(tlvh)) { + switch (ntohs(tlvh->type)) { + case EXT_TLV_PREFIX: + sum += show_vty_pref_info(vty, tlvh, length - sum); + break; + default: + sum += show_vty_unknown_tlv(vty, tlvh, length - sum); + break; + } + } +} diff --git a/ospfd/ospf_ext.h b/ospfd/ospf_ext.h new file mode 100644 index 0000000..535e548 --- /dev/null +++ b/ospfd/ospf_ext.h @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This is an implementation of RFC7684 OSPFv2 Prefix/Link Attribute + * Advertisement + * + * Module name: Extended Prefix/Link Opaque LSA header definition + * + * Author: Olivier Dugeon + * Author: Anselme Sawadogo + * + * Copyright (C) 2016 - 2018 Orange Labs http://www.orange.com + */ + +#ifndef _FRR_OSPF_EXT_PREF_H_ +#define _FRR_OSPF_EXT_PREF_H_ + +/* + * Opaque LSA's link state ID for Extended Prefix/Link is + * structured as follows. + * + * 24 16 8 0 + * +--------+--------+--------+--------+ + * | 7/8 |........|........|........| + * +--------+--------+--------+--------+ + * |<-Type->|<------- Instance ------->| + * + * + * Type: IANA has assigned '7' for Extended Prefix Opaque LSA + * and '8' for Extended Link Opaque LSA + * Instance: User may select arbitrary 24-bit values to identify + * different instances of Extended Prefix/Link Opaque LSA + * + */ + +/* + * 24 16 8 0 + * +--------+--------+--------+--------+ --- + * | LS age |Options | 10,11 | A + * +--------+--------+--------+--------+ | Standard (Opaque) LSA header; + * | 7/8 | Instance | | + * +--------+--------+--------+--------+ | Type 10 or 11 are used for Extended + * | Advertising router | | Prefix Opaque LSA + * +--------+--------+--------+--------+ | + * | LS sequence number | | Type 10 only is used for Extended + * +--------+--------+--------+--------+ | Link Opaque LSA + * | LS checksum | Length | V + * +--------+--------+--------+--------+ --- + * | Type | Length | A + * +--------+--------+--------+--------+ | TLV part for Extended Prefix/Link + * | | | Opaque LSA; + * ~ Values ... ~ | Values might be structured as a set + * | | V of sub-TLVs. + * +--------+--------+--------+--------+ --- + */ + +/* Global use constant numbers */ + +#define MAX_LEGAL_EXT_INSTANCE_NUM (0xffff) +#define LEGAL_EXT_INSTANCE_RANGE(i) (0 <= (i) && (i) <= 0xffff) + +/* Flags to manage Extended Link/Prefix Opaque LSA */ +#define EXT_LPFLG_LSA_INACTIVE 0x00 +#define EXT_LPFLG_LSA_ACTIVE 0x01 +#define EXT_LPFLG_LSA_ENGAGED 0x02 +#define EXT_LPFLG_LSA_LOOKUP_DONE 0x04 +#define EXT_LPFLG_LSA_FORCED_REFRESH 0x08 +#define EXT_LPFLG_FIB_ENTRY_SET 0x10 + +/* + * Following section defines TLV (tag, length, value) structures, + * used in Extended Prefix/Link Opaque LSA. + */ + +/* Extended Prefix TLV Route Types */ +#define EXT_TLV_PREF_ROUTE_UNSPEC 0 +#define EXT_TLV_PREF_ROUTE_INTRA_AREA 1 +#define EXT_TLV_PREF_ROUTE_INTER_AREA 3 +#define EXT_TLV_PREF_ROUTE_AS_EXT 5 +#define EXT_TLV_PREF_ROUTE_NSSA_EXT 7 + +/* + * Extended Prefix and Extended Prefix Range TLVs' + * Address family flag for IPv4 + */ +#define EXT_TLV_PREF_AF_IPV4 0 + +/* Extended Prefix TLV Flags */ +#define EXT_TLV_PREF_AFLG 0x80 +#define EXT_TLV_PREF_NFLG 0x40 + +/* Extended Prefix Range TLV Flags */ +#define EXT_TLV_PREF_RANGE_IAFLG 0x80 + +/* ERO subtlvs Flags */ +#define EXT_SUBTLV_ERO_LFLG 0x80 + +/* Extended Prefix TLV see RFC 7684 section 2.1 */ +#define EXT_TLV_PREFIX 1 +#define EXT_TLV_PREFIX_SIZE 8 +struct ext_tlv_prefix { + struct tlv_header header; + uint8_t route_type; + uint8_t pref_length; + uint8_t af; + uint8_t flags; + struct in_addr address; +}; + +/* Extended Link TLV see RFC 7684 section 3.1 */ +#define EXT_TLV_LINK 1 +#define EXT_TLV_LINK_SIZE 12 +struct ext_tlv_link { + struct tlv_header header; + uint8_t link_type; + uint8_t reserved[3]; + struct in_addr link_id; + struct in_addr link_data; +}; + +/* Remote Interface Address Sub-TLV, Cisco experimental use Sub-TLV */ +#define EXT_SUBTLV_RMT_ITF_ADDR 32768 +#define EXT_SUBTLV_RMT_ITF_ADDR_SIZE 4 +struct ext_subtlv_rmt_itf_addr { + struct tlv_header header; + struct in_addr value; +}; + +/* Internal structure to manage Extended Link/Prefix Opaque LSA */ +struct ospf_ext_lp { + bool enabled; + + /* Flags to manage this Extended Prefix/Link Opaque LSA */ + uint32_t flags; + + /* + * Scope is area Opaque Type 10 or AS Opaque LSA Type 11 for + * Extended Prefix and area Opaque Type 10 for Extended Link + */ + uint8_t scope; + + /* List of interface with Segment Routing enable */ + struct list *iflist; +}; + +/* Structure to aggregate interfaces information for Extended Prefix/Link */ +struct ext_itf { + /* 24-bit Opaque-ID field value according to RFC 7684 specification */ + uint32_t instance; + uint8_t type; /* Extended Prefix (7) or Link (8) */ + + /* Reference pointer to a Zebra-interface. */ + struct interface *ifp; + + /* Area info in which this SR link belongs to. */ + struct ospf_area *area; + + /* Flags to manage this link parameters. */ + uint32_t flags; + + /* SID type: Node, Adjacency or LAN Adjacency */ + enum sid_type stype; + + /* extended link/prefix TLV information */ + struct ext_tlv_prefix prefix; + struct ext_subtlv_prefix_sid node_sid; + struct ext_tlv_link link; + struct ext_subtlv_adj_sid adj_sid[2]; + struct ext_subtlv_lan_adj_sid lan_sid[2]; + + /* cisco experimental subtlv */ + struct ext_subtlv_rmt_itf_addr rmt_itf_addr; +}; + +/* Prototypes. */ +extern int ospf_ext_init(void); +extern void ospf_ext_term(void); +extern void ospf_ext_finish(void); +extern void ospf_ext_update_sr(bool enable); +extern void ospf_ext_link_srlb_update(void); +extern uint32_t ospf_ext_schedule_prefix_index(struct interface *ifp, + uint32_t index, + struct prefix_ipv4 *p, + uint8_t flags); +#endif /* _FRR_OSPF_EXT_PREF_H_ */ diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c new file mode 100644 index 0000000..dd8c926 --- /dev/null +++ b/ospfd/ospf_flood.c @@ -0,0 +1,1262 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF Flooding -- RFC2328 Section 13. + * Copyright (C) 1999, 2000 Toshiaki Takada + */ + +#include + +#include "monotime.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "command.h" +#include "table.h" +#include "frrevent.h" +#include "memory.h" +#include "log.h" +#include "zclient.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_dump.h" + +extern struct zclient *zclient; + +/** @brief Function to refresh type-5 and type-7 DNA + * LSAs when we receive an indication LSA. + * @param Ospf instance. + * @return Void. + */ +void ospf_refresh_dna_type5_and_type7_lsas(struct ospf *ospf) +{ + struct route_node *rn; + struct ospf_lsa *lsa = NULL; + + LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) + if (IS_LSA_SELF(lsa) && + CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE)) + ospf_lsa_refresh(ospf, lsa); + + LSDB_LOOP (NSSA_LSDB(ospf), rn, lsa) + if (IS_LSA_SELF(lsa) && + CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE)) + ospf_lsa_refresh(ospf, lsa); +} + +/** @brief Function to update area flood reduction states. + * @param area pointer. + * @return Void. + */ +void ospf_area_update_fr_state(struct ospf_area *area) +{ + unsigned int count_router_lsas = 0; + + if (area == NULL) + return; + + count_router_lsas = + (unsigned int)(ospf_lsdb_count(area->lsdb, OSPF_ROUTER_LSA) - + ospf_lsdb_count_self(area->lsdb, + OSPF_ROUTER_LSA)); + + if (count_router_lsas > + (unsigned int)area->fr_info.router_lsas_recv_dc_bit) { + area->fr_info.enabled = false; + area->fr_info.area_dc_clear = true; + return; + } else if (count_router_lsas < + (unsigned int)area->fr_info.router_lsas_recv_dc_bit) { + /* This can never happen, total number of router lsas received + * can never be less than router lsas received with dc bit set + */ + OSPF_LOG_ERR("%s: Counter mismatch for area %pI4", __func__, + &area->area_id); + OSPF_LOG_ERR( + "%s: router LSAs in lsdb %d router LSAs recvd with dc bit set %d", + __func__, count_router_lsas, + area->fr_info.router_lsas_recv_dc_bit); + return; + } + + area->fr_info.area_dc_clear = false; + + if (OSPF_FR_CONFIG(area->ospf, area)) { + if (!area->fr_info.enabled) { + area->fr_info.enabled = true; + area->fr_info.state_changed = true; + } + } else { + area->fr_info.enabled = false; + area->fr_info.area_dc_clear = true; + } +} + +/* Do the LSA acking specified in table 19, Section 13.5, row 2 + * This get called from ospf_flood_out_interface. Declared inline + * for speed. */ +static void ospf_flood_delayed_lsa_ack(struct ospf_neighbor *inbr, + struct ospf_lsa *lsa) +{ + /* LSA is more recent than database copy, but was not + flooded back out receiving interface. Delayed + acknowledgment sent. If interface is in Backup state + delayed acknowledgment sent only if advertisement + received from Designated Router, otherwise do nothing See + RFC 2328 Section 13.5 */ + + /* Whether LSA is more recent or not, and whether this is in + response to the LSA being sent out recieving interface has been + worked out previously */ + + /* Deal with router as BDR */ + if (inbr->oi->state == ISM_Backup && !NBR_IS_DR(inbr)) + return; + + /* Schedule a delayed LSA Ack to be sent */ + listnode_add(inbr->oi->ls_ack, + ospf_lsa_lock(lsa)); /* delayed LSA Ack */ +} + +/* Check LSA is related to external info. */ +struct external_info *ospf_external_info_check(struct ospf *ospf, + struct ospf_lsa *lsa) +{ + struct as_external_lsa *al; + struct prefix_ipv4 p; + struct route_node *rn; + struct list *ext_list; + struct listnode *node; + struct ospf_external *ext; + int type; + + al = (struct as_external_lsa *)lsa->data; + + p.family = AF_INET; + p.prefix = lsa->data->id; + p.prefixlen = ip_masklen(al->mask); + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + int redist_on = 0; + + redist_on = + is_default_prefix4(&p) + ? vrf_bitmap_check( + &zclient->default_information[AFI_IP], + ospf->vrf_id) + : (zclient->mi_redist[AFI_IP][type].enabled || + vrf_bitmap_check( + &zclient->redist[AFI_IP][type], + ospf->vrf_id)); + // Pending: check for MI above. + if (redist_on) { + ext_list = ospf->external[type]; + if (!ext_list) + continue; + + for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) { + rn = NULL; + if (ext->external_info) + rn = route_node_lookup( + ext->external_info, + (struct prefix *)&p); + if (rn) { + route_unlock_node(rn); + if (rn->info != NULL) + return (struct external_info *) + rn->info; + } + } + } + } + + if (is_default_prefix4(&p) && ospf->external[DEFAULT_ROUTE]) { + ext_list = ospf->external[DEFAULT_ROUTE]; + + for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) { + if (!ext->external_info) + continue; + + rn = route_node_lookup(ext->external_info, + (struct prefix *)&p); + if (!rn) + continue; + route_unlock_node(rn); + if (rn->info != NULL) + return (struct external_info *)rn->info; + } + } + return NULL; +} + +static void ospf_process_self_originated_lsa(struct ospf *ospf, + struct ospf_lsa *new, + struct ospf_area *area) +{ + struct ospf_interface *oi; + struct external_info *ei; + struct listnode *node; + struct as_external_lsa *al; + struct prefix_ipv4 p; + struct ospf_external_aggr_rt *aggr; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s:LSA[Type%d:%pI4]: Process self-originated LSA seq 0x%x", + ospf_get_name(ospf), new->data->type, + &new->data->id, ntohl(new->data->ls_seqnum)); + + /* If we're here, we installed a self-originated LSA that we received + from a neighbor, i.e. it's more recent. We must see whether we want + to originate it. + If yes, we should use this LSA's sequence number and reoriginate + a new instance. + if not --- we must flush this LSA from the domain. */ + switch (new->data->type) { + case OSPF_ROUTER_LSA: + /* Originate a new instance and schedule flooding */ + if (area->router_lsa_self) + area->router_lsa_self->data->ls_seqnum = + new->data->ls_seqnum; + ospf_router_lsa_update_area(area); + return; + case OSPF_NETWORK_LSA: + case OSPF_OPAQUE_LINK_LSA: + /* We must find the interface the LSA could belong to. + If the interface is no more a broadcast type or we are no + more + the DR, we flush the LSA otherwise -- create the new instance + and + schedule flooding. */ + + /* Look through all interfaces, not just area, since interface + could be moved from one area to another. */ + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) + /* These are sanity check. */ + if (IPV4_ADDR_SAME(&oi->address->u.prefix4, + &new->data->id)) { + if (oi->area != area + || oi->type != OSPF_IFTYPE_BROADCAST + || !IPV4_ADDR_SAME(&oi->address->u.prefix4, + &DR(oi))) { + ospf_schedule_lsa_flush_area(area, new); + return; + } + + if (new->data->type == OSPF_OPAQUE_LINK_LSA) { + ospf_opaque_lsa_refresh(new); + return; + } + + if (oi->network_lsa_self) + oi->network_lsa_self->data->ls_seqnum = + new->data->ls_seqnum; + /* Schedule network-LSA origination. */ + ospf_network_lsa_update(oi); + return; + } + break; + case OSPF_SUMMARY_LSA: + case OSPF_ASBR_SUMMARY_LSA: + ospf_schedule_abr_task(ospf); + break; + case OSPF_AS_EXTERNAL_LSA: + case OSPF_AS_NSSA_LSA: + if ((new->data->type == OSPF_AS_EXTERNAL_LSA) + && CHECK_FLAG(new->flags, OSPF_LSA_LOCAL_XLT)) { + ospf_translated_nssa_refresh(ospf, NULL, new); + return; + } + + al = (struct as_external_lsa *)new->data; + p.family = AF_INET; + p.prefixlen = ip_masklen(al->mask); + p.prefix = new->data->id; + + ei = ospf_external_info_check(ospf, new); + if (ei) { + if (ospf_external_aggr_match(ospf, &ei->p)) { + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "%s, Matching external aggregate route found for %pI4, so don't refresh it.", + __func__, + &ei->p.prefix); + + /* Aggregated external route shouldn't + * be in LSDB. + */ + if (!IS_LSA_MAXAGE(new)) + ospf_lsa_flush_as(ospf, new); + + return; + } + + ospf_external_lsa_refresh(ospf, new, ei, + LSA_REFRESH_FORCE, false); + } else { + aggr = (struct ospf_external_aggr_rt *) + ospf_extrenal_aggregator_lookup(ospf, &p); + if (aggr) { + struct external_info ei_aggr; + + memset(&ei_aggr, 0, + sizeof(struct external_info)); + ei_aggr.p = aggr->p; + ei_aggr.tag = aggr->tag; + ei_aggr.instance = ospf->instance; + ei_aggr.route_map_set.metric = -1; + ei_aggr.route_map_set.metric_type = -1; + + ospf_external_lsa_refresh(ospf, new, &ei_aggr, + LSA_REFRESH_FORCE, true); + SET_FLAG(aggr->flags, + OSPF_EXTERNAL_AGGRT_ORIGINATED); + } else + ospf_lsa_flush_as(ospf, new); + } + break; + case OSPF_OPAQUE_AREA_LSA: + ospf_opaque_lsa_refresh(new); + break; + case OSPF_OPAQUE_AS_LSA: + ospf_opaque_lsa_refresh(new); + /* Reconsideration may needed. */ /* XXX */ + break; + default: + break; + } +} + +/* OSPF LSA flooding -- RFC2328 Section 13.(5). */ + +/* Now Updated for NSSA operation, as follows: + + + Type-5's have no change. Blocked to STUB or NSSA. + + Type-7's can be received, and if a DR + they will also flood the local NSSA Area as Type-7's + + If a Self-Originated LSA (now an ASBR), + The LSDB will be updated as Type-5's, (for continual re-fresh) + + If an NSSA-IR it is installed/flooded as Type-7, P-bit on. + if an NSSA-ABR it is installed/flooded as Type-7, P-bit off. + + Later, during the ABR TASK, if the ABR is the Elected NSSA + translator, then All Type-7s (with P-bit ON) are Translated to + Type-5's and flooded to all non-NSSA/STUB areas. + + During ASE Calculations, + non-ABRs calculate external routes from Type-7's + ABRs calculate external routes from Type-5's and non-self Type-7s +*/ +int ospf_flood(struct ospf *ospf, struct ospf_neighbor *nbr, + struct ospf_lsa *current, struct ospf_lsa *new) +{ + struct ospf_interface *oi; + int lsa_ack_flag; + + /* Type-7 LSA's will be flooded throughout their native NSSA area, + but will also be flooded as Type-5's into ABR capable links. */ + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s:LSA[Flooding]: start, NBR %pI4 (%s), cur(%p), New-LSA[%s]", + ospf_get_name(ospf), &nbr->router_id, + lookup_msg(ospf_nsm_state_msg, nbr->state, NULL), + (void *)current, dump_lsa_key(new)); + + oi = nbr->oi; + + /* If there is already a database copy, and if the + database copy was received via flooding and installed less + than MinLSArrival seconds ago, discard the new LSA + (without acknowledging it). */ + if (current != NULL) /* -- endo. */ + { + if (IS_LSA_SELF(current) + && (ntohs(current->data->ls_age) == 0 + && ntohl(current->data->ls_seqnum) + == OSPF_INITIAL_SEQUENCE_NUMBER)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s:LSA[Flooding]: Got a self-originated LSA, while local one is initial instance.", + ospf_get_name(ospf)); + ; /* Accept this LSA for quick LSDB resynchronization. + */ + } else if (monotime_since(¤t->tv_recv, NULL) + < ospf->min_ls_arrival * 1000LL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s:LSA[Flooding]: LSA is received recently.", + ospf_get_name(ospf)); + return -1; + } + } + + /* Flood the new LSA out some subset of the router's interfaces. + In some cases (e.g., the state of the receiving interface is + DR and the LSA was received from a router other than the + Backup DR) the LSA will be flooded back out the receiving + interface. */ + lsa_ack_flag = ospf_flood_through(ospf, nbr, new); + + /* Remove the current database copy from all neighbors' Link state + retransmission lists. AS_EXTERNAL and AS_EXTERNAL_OPAQUE does + ^^^^^^^^^^^^^^^^^^^^^^^ + not have area ID. + All other (even NSSA's) do have area ID. */ + if (current) { + switch (current->data->type) { + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + ospf_ls_retransmit_delete_nbr_as(ospf, current); + break; + default: + ospf_ls_retransmit_delete_nbr_area(oi->area, current); + break; + } + } + + /* Do some internal house keeping that is needed here */ + SET_FLAG(new->flags, OSPF_LSA_RECEIVED); + (void)ospf_lsa_is_self_originated(ospf, new); /* Let it set the flag */ + + /* Received non-self-originated Grace LSA */ + if (IS_GRACE_LSA(new) && !IS_LSA_SELF(new)) { + + if (IS_LSA_MAXAGE(new)) { + + /* Handling Max age grace LSA.*/ + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, Received a maxage GRACE-LSA from router %pI4", + __func__, &new->data->adv_router); + + if (current) { + ospf_process_maxage_grace_lsa(ospf, new, nbr); + } else { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, Grace LSA doesn't exist in lsdb, so discarding grace lsa", + __func__); + return -1; + } + } else { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, Received a GRACE-LSA from router %pI4", + __func__, &new->data->adv_router); + + if (ospf_process_grace_lsa(ospf, new, nbr) + == OSPF_GR_NOT_HELPER) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, Not moving to HELPER role, So discarding grace LSA", + __func__); + return -1; + } + } + } + + /* Install the new LSA in the link state database + (replacing the current database copy). This may cause the + routing table calculation to be scheduled. In addition, + timestamp the new LSA with the current time. The flooding + procedure cannot overwrite the newly installed LSA until + MinLSArrival seconds have elapsed. */ + + if (!(new = ospf_lsa_install(ospf, oi, new))) + return -1; /* unknown LSA type or any other error condition */ + + /* check if the installed LSA is an indication LSA */ + if (ospf_check_indication_lsa(new) && !IS_LSA_SELF(new) && + !IS_LSA_MAXAGE(new)) { + new->area->fr_info.area_ind_lsa_recvd = true; + /* check if there are already type 5 LSAs originated + * with DNA bit set, if yes reoriginate those LSAs. + */ + ospf_refresh_dna_type5_and_type7_lsas(ospf); + } + + /* Check if we recived an indication LSA flush on backbone + * network. + */ + ospf_recv_indication_lsa_flush(new); + + if (new->area && OSPF_FR_CONFIG(ospf, new->area)) { + struct lsa_header const *lsah = new->data; + + if (!CHECK_FLAG(lsah->options, OSPF_OPTION_DC) && + !ospf_check_indication_lsa(new)) { + + new->area->fr_info.area_dc_clear = true; + /* check of previously area supported flood reduction */ + if (new->area->fr_info.enabled) { + new->area->fr_info.enabled = false; + OSPF_LOG_DEBUG( + IS_DEBUG_OSPF_EVENT, + "Flood Reduction STATE on -> off by %s LSA", + dump_lsa_key(new)); + /* if yes update all the lsa to the area the + * new LSAs will have DNA bit set to 0. + */ + ospf_refresh_area_self_lsas(new->area); + } + } else if (!new->area->fr_info.enabled) { + /* check again after installing new LSA that area + * supports flood reduction. + */ + ospf_area_update_fr_state(new->area); + if (new->area->fr_info.enabled) { + OSPF_LOG_DEBUG( + IS_DEBUG_OSPF_EVENT, + "Flood Reduction STATE off -> on by %s LSA", + dump_lsa_key(new)); + ospf_refresh_area_self_lsas(new->area); + } + } + } + + /* Acknowledge the receipt of the LSA by sending a Link State + Acknowledgment packet back out the receiving interface. */ + if (lsa_ack_flag) + ospf_flood_delayed_lsa_ack(nbr, new); + + /* If this new LSA indicates that it was originated by the + receiving router itself, the router must take special action, + either updating the LSA or in some cases flushing it from + the routing domain. */ + if (ospf_lsa_is_self_originated(ospf, new)) + ospf_process_self_originated_lsa(ospf, new, oi->area); + else + /* Update statistics value for OSPF-MIB. */ + ospf->rx_lsa_count++; + + return 0; +} + +/* OSPF LSA flooding -- RFC2328 Section 13.3. */ +int ospf_flood_through_interface(struct ospf_interface *oi, + struct ospf_neighbor *inbr, + struct ospf_lsa *lsa) +{ + struct ospf_neighbor *onbr; + struct route_node *rn; + int retx_flag; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: considering int %s (%s), INBR(%pI4), LSA[%s] AGE %u", + __func__, IF_NAME(oi), ospf_get_name(oi->ospf), + inbr ? &inbr->router_id : NULL, dump_lsa_key(lsa), + ntohs(lsa->data->ls_age)); + + if (!ospf_if_is_enable(oi)) + return 0; + + if (IS_OPAQUE_LSA(lsa->data->type) && + !OSPF_IF_PARAM(oi, opaque_capable)) { + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "%s: Skipping interface %s (%s) with opaque disabled.", + __func__, IF_NAME(oi), ospf_get_name(oi->ospf)); + return 0; + } + + /* If flood reduction is configured, set the DC bit on the lsa. */ + if (IS_LSA_SELF(lsa)) { + if (OSPF_FR_CONFIG(oi->area->ospf, oi->area)) { + if (!ospf_check_indication_lsa(lsa)) { + SET_FLAG(lsa->data->options, OSPF_OPTION_DC); + ospf_lsa_checksum(lsa->data); + } + } else if (CHECK_FLAG(lsa->data->options, OSPF_OPTION_DC)) { + UNSET_FLAG(lsa->data->options, OSPF_OPTION_DC); + ospf_lsa_checksum(lsa->data); + } + + /* If flood reduction is enabled then set DNA bit on the + * self lsas. + */ + if (oi->area->fr_info.enabled) + SET_FLAG(lsa->data->ls_age, DO_NOT_AGE); + } + + /* Remember if new LSA is added to a retransmit list. */ + retx_flag = 0; + + /* Each of the neighbors attached to this interface are examined, + to determine whether they must receive the new LSA. The following + steps are executed for each neighbor: */ + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + struct ospf_lsa *ls_req; + + if (rn->info == NULL) + continue; + + onbr = rn->info; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: considering nbr %pI4 via %s (%s), state: %s", + __func__, &onbr->router_id, IF_NAME(oi), + ospf_get_name(oi->ospf), + lookup_msg(ospf_nsm_state_msg, onbr->state, + NULL)); + + /* If the neighbor is in a lesser state than Exchange, it + does not participate in flooding, and the next neighbor + should be examined. */ + if (onbr->state < NSM_Exchange) + continue; + + /* If the adjacency is not yet full (neighbor state is + Exchange or Loading), examine the Link state request + list associated with this adjacency. If there is an + instance of the new LSA on the list, it indicates that + the neighboring router has an instance of the LSA + already. Compare the new LSA to the neighbor's copy: */ + if (onbr->state < NSM_Full) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: adj to onbr %pI4 is not Full (%s)", + __func__, &onbr->router_id, + lookup_msg(ospf_nsm_state_msg, + onbr->state, NULL)); + ls_req = ospf_ls_request_lookup(onbr, lsa); + if (ls_req != NULL) { + int ret; + + ret = ospf_lsa_more_recent(ls_req, lsa); + /* The new LSA is less recent. */ + if (ret > 0) + continue; + /* The two copies are the same instance, then + delete + the LSA from the Link state request list. */ + else if (ret == 0) { + ospf_ls_request_delete(onbr, ls_req); + ospf_check_nbr_loading(onbr); + continue; + } + /* The new LSA is more recent. Delete the LSA + from the Link state request list. */ + else { + ospf_ls_request_delete(onbr, ls_req); + ospf_check_nbr_loading(onbr); + } + } + } + + if (IS_OPAQUE_LSA(lsa->data->type)) { + if (!CHECK_FLAG(onbr->options, OSPF_OPTION_O)) { + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "%s: Skipping neighbor %s via %pI4 -- Not Opaque-capable.", + __func__, IF_NAME(oi), + &onbr->router_id); + continue; + } + } + + /* If the new LSA was received from this neighbor, + examine the next neighbor. */ + if (inbr) { + /* + * Triggered by LSUpd message parser "ospf_ls_upd ()". + * E.g., all LSAs handling here is received via network. + */ + if (IPV4_ADDR_SAME(&inbr->router_id, + &onbr->router_id)) { + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "%s: Skipping neighbor %s via %pI4 -- inbr == onbr.", + __func__, IF_NAME(oi), + &inbr->router_id); + continue; + } + } else { + /* + * Triggered by MaxAge remover, so far. + * NULL "inbr" means flooding starts from this node. + */ + if (IPV4_ADDR_SAME(&lsa->data->adv_router, + &onbr->router_id)) { + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "%s: Skipping neighbor %s via %pI4 -- lsah->adv_router == onbr.", + __func__, IF_NAME(oi), + &onbr->router_id); + continue; + } + } + + /* Add the new LSA to the Link state retransmission list + for the adjacency. The LSA will be retransmitted + at intervals until an acknowledgment is seen from + the neighbor. */ + ospf_ls_retransmit_add(onbr, lsa); + retx_flag = 1; + } + + /* If in the previous step, the LSA was NOT added to any of + the Link state retransmission lists, there is no need to + flood the LSA out the interface. */ + if (retx_flag == 0) { + return (inbr && inbr->oi == oi); + } + + /* if we've received the lsa on this interface we need to perform + additional checking */ + if (inbr && (inbr->oi == oi)) { + /* If the new LSA was received on this interface, and it was + received from either the Designated Router or the Backup + 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(lsa, LSA_FLOODING)) + zlog_debug("%s: DR/BDR NOT SEND to int %s (%s)", + __func__, IF_NAME(oi), + ospf_get_name(oi->ospf)); + return 1; + } + + /* If the new LSA was received on this interface, and the + interface state is Backup, examine the next interface. The + Designated Router will do the flooding on this interface. + However, if the Designated Router fails the router will + end up retransmitting the updates. */ + + if (oi->state == ISM_Backup) { + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "%s: ISM_Backup NOT SEND to int %s (%s)", + __func__, IF_NAME(oi), + ospf_get_name(oi->ospf)); + return 1; + } + } + + /* The LSA must be flooded out the interface. Send a Link State + Update packet (including the new LSA as contents) out the + interface. The LSA's LS age must be incremented by InfTransDelay + (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). */ + 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)); + + /* RFC2328 Section 13.3 + On non-broadcast networks, separate Link State Update + packets must be sent, as unicasts, to each adjacent neighbor + (i.e., those in state Exchange or greater). The destination + IP addresses for these packets are the neighbors' IP + addresses. */ + if (oi->type == OSPF_IFTYPE_NBMA) { + struct ospf_neighbor *nbr; + + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + nbr = rn->info; + + if (!nbr) + continue; + if (nbr != oi->nbr_self && nbr->state >= NSM_Exchange) + ospf_ls_upd_send_lsa(nbr, lsa, + OSPF_SEND_PACKET_DIRECT); + } + } else + /* If P2MP delayed reflooding is configured and the LSA was + received from a neighbor on the P2MP interface, do not flood + if back out on the interface. The LSA will be retransmitted + upon expiration of each neighbor's retransmission timer. This + will allow time to receive a multicast multicast link state + acknoweldgement and remove the LSA from each neighbor's link + state retransmission list. */ + if (oi->p2mp_delay_reflood && + (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) && + (inbr != NULL) && (oi == inbr->oi)) { + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "Delay reflooding for LSA[%s] from NBR %pI4 on interface %s", + dump_lsa_key(lsa), + inbr ? &(inbr->router_id) + : &(oi->ospf->router_id), + IF_NAME(oi)); + } else + ospf_ls_upd_send_lsa(oi->nbr_self, lsa, + OSPF_SEND_PACKET_INDIRECT); + + return 0; +} + +int ospf_flood_through_area(struct ospf_area *area, struct ospf_neighbor *inbr, + struct ospf_lsa *lsa) +{ + struct listnode *node, *nnode; + struct ospf_interface *oi; + int lsa_ack_flag = 0; + + assert(area); + /* All other types are specific to a single area (Area A). The + eligible interfaces are all those interfaces attaching to the + Area A. If Area A is the backbone, this includes all the virtual + links. */ + for (ALL_LIST_ELEMENTS(area->oiflist, node, nnode, oi)) { + if (area->area_id.s_addr != OSPF_AREA_BACKBONE + && oi->type == OSPF_IFTYPE_VIRTUALLINK) + continue; + + if ((lsa->data->type == OSPF_OPAQUE_LINK_LSA) + && (lsa->oi != oi)) { + /* + * Link local scoped Opaque-LSA should only be flooded + * for the link on which the LSA has received. + */ + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "Type-9 Opaque-LSA: lsa->oi(%p) != oi(%p)", + (void *)lsa->oi, (void *)oi); + continue; + } + + if (ospf_flood_through_interface(oi, inbr, lsa)) + lsa_ack_flag = 1; + } + + return (lsa_ack_flag); +} + +int ospf_flood_through_as(struct ospf *ospf, struct ospf_neighbor *inbr, + struct ospf_lsa *lsa) +{ + struct listnode *node; + struct ospf_area *area; + int lsa_ack_flag; + + lsa_ack_flag = 0; + + /* The incoming LSA is type 5 or type 7 (AS-EXTERNAL or AS-NSSA ) + + Divert the Type-5 LSA's to all non-NSSA/STUB areas + + Divert the Type-7 LSA's to all NSSA areas + + AS-external-LSAs are flooded throughout the entire AS, with the + exception of stub areas (see Section 3.6). The eligible + interfaces are all the router's interfaces, excluding virtual + links and those interfaces attaching to stub areas. */ + + if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT)) /* Translated from 7 */ + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("Flood/AS: NSSA TRANSLATED LSA"); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + int continue_flag = 0; + struct listnode *if_node; + struct ospf_interface *oi; + + switch (area->external_routing) { + /* Don't send AS externals into stub areas. Various types + of support for partial stub areas can be implemented + here. NSSA's will receive Type-7's that have areas + matching the originl LSA. */ + case OSPF_AREA_NSSA: /* Sending Type 5 or 7 into NSSA area */ + /* Type-7, flood NSSA area */ + if (lsa->data->type == OSPF_AS_NSSA_LSA + && area == lsa->area) + /* We will send it. */ + continue_flag = 0; + else + continue_flag = 1; /* Skip this NSSA area for + Type-5's et al */ + break; + + case OSPF_AREA_TYPE_MAX: + case OSPF_AREA_STUB: + continue_flag = 1; /* Skip this area. */ + break; + + case OSPF_AREA_DEFAULT: + default: + /* No Type-7 into normal area */ + if (lsa->data->type == OSPF_AS_NSSA_LSA) + continue_flag = 1; /* skip Type-7 */ + else + continue_flag = 0; /* Do this area. */ + break; + } + + /* Do continue for above switch. Saves a big if then mess */ + if (continue_flag) + continue; /* main for-loop */ + + /* send to every interface in this area */ + + for (ALL_LIST_ELEMENTS_RO(area->oiflist, if_node, oi)) { + /* Skip virtual links */ + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + if (ospf_flood_through_interface(oi, inbr, + lsa)) /* lsa */ + lsa_ack_flag = 1; + } + } /* main area for-loop */ + + return (lsa_ack_flag); +} + +int ospf_flood_through(struct ospf *ospf, struct ospf_neighbor *inbr, + struct ospf_lsa *lsa) +{ + int lsa_ack_flag = 0; + + /* Type-7 LSA's for NSSA are flooded throughout the AS here, and + upon return are updated in the LSDB for Type-7's. Later, + re-fresh will re-send them (and also, if ABR, packet code will + translate to Type-5's) + + As usual, Type-5 LSA's (if not DISCARDED because we are STUB or + NSSA) are flooded throughout the AS, and are updated in the + global table. */ + /* + * At the common sub-sub-function "ospf_flood_through_interface()", + * a parameter "inbr" will be used to distinguish the called context + * whether the given LSA was received from the neighbor, or the + * flooding for the LSA starts from this node (e.g. the LSA was self- + * originated, or the LSA is going to be flushed from routing domain). + * + * So, for consistency reasons, this function "ospf_flood_through()" + * should also allow the usage that the given "inbr" parameter to be + * NULL. If we do so, corresponding AREA parameter should be referred + * by "lsa->area", instead of "inbr->oi->area". + */ + switch (lsa->data->type) { + case OSPF_AS_EXTERNAL_LSA: /* Type-5 */ + case OSPF_OPAQUE_AS_LSA: + lsa_ack_flag = ospf_flood_through_as(ospf, inbr, lsa); + break; + /* Type-7 Only received within NSSA, then flooded */ + case OSPF_AS_NSSA_LSA: + /* Any P-bit was installed with the Type-7. */ + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: LOCAL NSSA FLOOD of Type-7.", __func__); + /* Fallthrough */ + default: + lsa_ack_flag = ospf_flood_through_area(lsa->area, inbr, lsa); + break; + } + + /* always need to send ack when incoming intf is PTP or P2MP */ + if (inbr != NULL && (inbr->oi->type == OSPF_IFTYPE_POINTOMULTIPOINT || + inbr->oi->type == OSPF_IFTYPE_POINTOPOINT)) + lsa_ack_flag = 1; + + return (lsa_ack_flag); +} + + +/* Management functions for neighbor's Link State Request list. */ +void ospf_ls_request_add(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) +{ + /* + * We cannot make use of the newly introduced callback function + * "lsdb->new_lsa_hook" to replace debug output below, just because + * it seems no simple and smart way to pass neighbor information to + * the common function "ospf_lsdb_add()" -- endo. + */ + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug("RqstL(%lu)++, NBR(%pI4(%s)), LSA[%s]", + ospf_ls_request_count(nbr), + &nbr->router_id, + ospf_get_name(nbr->oi->ospf), dump_lsa_key(lsa)); + + ospf_lsdb_add(&nbr->ls_req, lsa); +} + +unsigned long ospf_ls_request_count(struct ospf_neighbor *nbr) +{ + return ospf_lsdb_count_all(&nbr->ls_req); +} + +int ospf_ls_request_isempty(struct ospf_neighbor *nbr) +{ + return ospf_lsdb_isempty(&nbr->ls_req); +} + +/* Remove LSA from neighbor's ls-request list. */ +void ospf_ls_request_delete(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) +{ + if (nbr->ls_req_last == lsa) { + ospf_lsa_unlock(&nbr->ls_req_last); + nbr->ls_req_last = NULL; + } + + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) /* -- endo. */ + zlog_debug("RqstL(%lu)--, NBR(%pI4(%s)), LSA[%s]", + ospf_ls_request_count(nbr), + &nbr->router_id, + ospf_get_name(nbr->oi->ospf), dump_lsa_key(lsa)); + + ospf_lsdb_delete(&nbr->ls_req, lsa); +} + +/* Remove all LSA from neighbor's ls-requenst list. */ +void ospf_ls_request_delete_all(struct ospf_neighbor *nbr) +{ + ospf_lsa_unlock(&nbr->ls_req_last); + nbr->ls_req_last = NULL; + ospf_lsdb_delete_all(&nbr->ls_req); +} + +/* Lookup LSA from neighbor's ls-request list. */ +struct ospf_lsa *ospf_ls_request_lookup(struct ospf_neighbor *nbr, + struct ospf_lsa *lsa) +{ + return ospf_lsdb_lookup(&nbr->ls_req, lsa); +} + +struct ospf_lsa *ospf_ls_request_new(struct lsa_header *lsah) +{ + struct ospf_lsa *new; + + new = ospf_lsa_new_and_data(OSPF_LSA_HEADER_SIZE); + memcpy(new->data, lsah, OSPF_LSA_HEADER_SIZE); + + return new; +} + + +/* Management functions for neighbor's ls-retransmit list. */ +unsigned long ospf_ls_retransmit_count(struct ospf_neighbor *nbr) +{ + return ospf_lsdb_count_all(&nbr->ls_rxmt); +} + +unsigned long ospf_ls_retransmit_count_self(struct ospf_neighbor *nbr, + int lsa_type) +{ + return ospf_lsdb_count_self(&nbr->ls_rxmt, lsa_type); +} + +int ospf_ls_retransmit_isempty(struct ospf_neighbor *nbr) +{ + return ospf_lsdb_isempty(&nbr->ls_rxmt); +} + +/* Add LSA to be retransmitted to neighbor's ls-retransmit list. */ +void ospf_ls_retransmit_add(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) +{ + struct ospf_lsa *old; + + old = ospf_ls_retransmit_lookup(nbr, lsa); + + if (ospf_lsa_more_recent(old, lsa) < 0) { + if (old) { + old->retransmit_counter--; + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug("RXmtL(%lu)--, NBR(%pI4(%s)), LSA[%s]", + ospf_ls_retransmit_count(nbr), + &nbr->router_id, + ospf_get_name(nbr->oi->ospf), + dump_lsa_key(old)); + ospf_lsdb_delete(&nbr->ls_rxmt, old); + } + lsa->retransmit_counter++; + /* + * We cannot make use of the newly introduced callback function + * "lsdb->new_lsa_hook" to replace debug output below, just + * because + * it seems no simple and smart way to pass neighbor information + * to + * the common function "ospf_lsdb_add()" -- endo. + */ + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug("RXmtL(%lu)++, NBR(%pI4(%s)), LSA[%s]", + ospf_ls_retransmit_count(nbr), + &nbr->router_id, + ospf_get_name(nbr->oi->ospf), + dump_lsa_key(lsa)); + ospf_lsdb_add(&nbr->ls_rxmt, lsa); + } +} + +/* Remove LSA from neibghbor's ls-retransmit list. */ +void ospf_ls_retransmit_delete(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) +{ + if (ospf_ls_retransmit_lookup(nbr, lsa)) { + lsa->retransmit_counter--; + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) /* -- endo. */ + zlog_debug("RXmtL(%lu)--, NBR(%pI4(%s)), LSA[%s]", + ospf_ls_retransmit_count(nbr), + &nbr->router_id, + ospf_get_name(nbr->oi->ospf), + dump_lsa_key(lsa)); + ospf_lsdb_delete(&nbr->ls_rxmt, lsa); + } +} + +/* Clear neighbor's ls-retransmit list. */ +void ospf_ls_retransmit_clear(struct ospf_neighbor *nbr) +{ + struct ospf_lsdb *lsdb; + int i; + + lsdb = &nbr->ls_rxmt; + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) { + struct route_table *table = lsdb->type[i].db; + struct route_node *rn; + struct ospf_lsa *lsa; + + for (rn = route_top(table); rn; rn = route_next(rn)) + if ((lsa = rn->info) != NULL) + ospf_ls_retransmit_delete(nbr, lsa); + } + + ospf_lsa_unlock(&nbr->ls_req_last); + nbr->ls_req_last = NULL; +} + +/* Lookup LSA from neighbor's ls-retransmit list. */ +struct ospf_lsa *ospf_ls_retransmit_lookup(struct ospf_neighbor *nbr, + struct ospf_lsa *lsa) +{ + return ospf_lsdb_lookup(&nbr->ls_rxmt, lsa); +} + +static void ospf_ls_retransmit_delete_nbr_if(struct ospf_interface *oi, + struct ospf_lsa *lsa) +{ + struct route_node *rn; + struct ospf_neighbor *nbr; + struct ospf_lsa *lsr; + + if (ospf_if_is_enable(oi)) + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + /* If LSA find in LS-retransmit list, then remove it. */ + nbr = rn->info; + + if (!nbr) + continue; + + lsr = ospf_ls_retransmit_lookup(nbr, lsa); + + /* If LSA find in ls-retransmit list, remove it. */ + if (lsr != NULL && + lsr->data->ls_seqnum == lsa->data->ls_seqnum) + ospf_ls_retransmit_delete(nbr, lsr); + } +} + +void ospf_ls_retransmit_delete_nbr_area(struct ospf_area *area, + struct ospf_lsa *lsa) +{ + struct listnode *node, *nnode; + struct ospf_interface *oi; + + for (ALL_LIST_ELEMENTS(area->oiflist, node, nnode, oi)) + ospf_ls_retransmit_delete_nbr_if(oi, lsa); +} + +void ospf_ls_retransmit_delete_nbr_as(struct ospf *ospf, struct ospf_lsa *lsa) +{ + struct listnode *node, *nnode; + struct ospf_interface *oi; + + for (ALL_LIST_ELEMENTS(ospf->oiflist, node, nnode, oi)) + ospf_ls_retransmit_delete_nbr_if(oi, lsa); +} + + +/* Sets ls_age to MaxAge and floods throu the area. + When we implement ASE routing, there will be another function + flushing an LSA from the whole domain. */ +void ospf_lsa_flush_area(struct ospf_lsa *lsa, struct ospf_area *area) +{ + struct ospf *ospf = area->ospf; + + if (ospf_lsa_is_self_originated(ospf, lsa) + && ospf->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "%s:LSA[Type%d:%pI4]: Graceful Restart in progress -- not flushing self-originated LSA", + ospf_get_name(ospf), lsa->data->type, + &lsa->data->id); + return; + } + + /* Reset the lsa origination time such that it gives + more time for the ACK to be received and avoid + retransmissions */ + lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: MaxAge set to LSA[%s]", __func__, + dump_lsa_key(lsa)); + monotime(&lsa->tv_recv); + lsa->tv_orig = lsa->tv_recv; + ospf_flood_through_area(area, NULL, lsa); + ospf_lsa_maxage(ospf, lsa); +} + +void ospf_lsa_flush_as(struct ospf *ospf, struct ospf_lsa *lsa) +{ + if (ospf_lsa_is_self_originated(ospf, lsa) + && ospf->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "%s:LSA[Type%d:%pI4]: Graceful Restart in progress -- not flushing self-originated LSA", + ospf_get_name(ospf), lsa->data->type, + &lsa->data->id); + return; + } + + /* Reset the lsa origination time such that it gives + more time for the ACK to be received and avoid + retransmissions */ + lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: MaxAge set to LSA[%s]", __func__, + dump_lsa_key(lsa)); + monotime(&lsa->tv_recv); + lsa->tv_orig = lsa->tv_recv; + ospf_flood_through_as(ospf, NULL, lsa); + ospf_lsa_maxage(ospf, lsa); +} + +void ospf_lsa_flush(struct ospf *ospf, struct ospf_lsa *lsa) +{ + lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + + switch (lsa->data->type) { + case OSPF_ROUTER_LSA: + case OSPF_NETWORK_LSA: + case OSPF_SUMMARY_LSA: + case OSPF_ASBR_SUMMARY_LSA: + case OSPF_AS_NSSA_LSA: + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + ospf_lsa_flush_area(lsa, lsa->area); + break; + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + ospf_lsa_flush_as(ospf, lsa); + break; + default: + zlog_info("%s: Unknown LSA type %u", __func__, lsa->data->type); + break; + } +} diff --git a/ospfd/ospf_flood.h b/ospfd/ospf_flood.h new file mode 100644 index 0000000..3757400 --- /dev/null +++ b/ospfd/ospf_flood.h @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF Flooding -- RFC2328 Section 13. + * Copyright (C) 1999, 2000 Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPF_FLOOD_H +#define _ZEBRA_OSPF_FLOOD_H + +extern int ospf_flood(struct ospf *, struct ospf_neighbor *, struct ospf_lsa *, + struct ospf_lsa *); +extern int ospf_flood_through(struct ospf *, struct ospf_neighbor *, + struct ospf_lsa *); +extern int ospf_flood_through_area(struct ospf_area *, struct ospf_neighbor *, + struct ospf_lsa *); +extern int ospf_flood_through_as(struct ospf *, struct ospf_neighbor *, + struct ospf_lsa *); +extern int ospf_flood_through_interface(struct ospf_interface *oi, + struct ospf_neighbor *inbr, + struct ospf_lsa *lsa); + +extern unsigned long ospf_ls_request_count(struct ospf_neighbor *); +extern int ospf_ls_request_isempty(struct ospf_neighbor *); +extern struct ospf_lsa *ospf_ls_request_new(struct lsa_header *); +extern void ospf_ls_request_free(struct ospf_lsa *); +extern void ospf_ls_request_add(struct ospf_neighbor *, struct ospf_lsa *); +extern void ospf_ls_request_delete(struct ospf_neighbor *, struct ospf_lsa *); +extern void ospf_ls_request_delete_all(struct ospf_neighbor *); +extern struct ospf_lsa *ospf_ls_request_lookup(struct ospf_neighbor *, + struct ospf_lsa *); + +extern unsigned long ospf_ls_retransmit_count(struct ospf_neighbor *); +extern unsigned long ospf_ls_retransmit_count_self(struct ospf_neighbor *, int); +extern int ospf_ls_retransmit_isempty(struct ospf_neighbor *); +extern void ospf_ls_retransmit_add(struct ospf_neighbor *, struct ospf_lsa *); +extern void ospf_ls_retransmit_delete(struct ospf_neighbor *, + struct ospf_lsa *); +extern void ospf_ls_retransmit_clear(struct ospf_neighbor *); +extern struct ospf_lsa *ospf_ls_retransmit_lookup(struct ospf_neighbor *, + struct ospf_lsa *); +extern void ospf_ls_retransmit_delete_nbr_area(struct ospf_area *, + struct ospf_lsa *); +extern void ospf_ls_retransmit_delete_nbr_as(struct ospf *, struct ospf_lsa *); +extern void ospf_ls_retransmit_add_nbr_all(struct ospf_interface *, + struct ospf_lsa *); + +extern void ospf_flood_lsa_area(struct ospf_lsa *, struct ospf_area *); +extern void ospf_flood_lsa_as(struct ospf_lsa *); +extern void ospf_lsa_flush_area(struct ospf_lsa *, struct ospf_area *); +extern void ospf_lsa_flush_as(struct ospf *, struct ospf_lsa *); +extern void ospf_lsa_flush(struct ospf *, struct ospf_lsa *); +extern struct external_info *ospf_external_info_check(struct ospf *, + struct ospf_lsa *); + +extern void ospf_lsdb_init(struct ospf_lsdb *); +extern void ospf_area_update_fr_state(struct ospf_area *area); +extern void ospf_refresh_dna_type5_and_type7_lsas(struct ospf *ospf); + +#endif /* _ZEBRA_OSPF_FLOOD_H */ diff --git a/ospfd/ospf_gr.c b/ospfd/ospf_gr.c new file mode 100644 index 0000000..c23c420 --- /dev/null +++ b/ospfd/ospf_gr.c @@ -0,0 +1,875 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This is an implementation of RFC 3623 Graceful OSPF Restart. + * + * Copyright 2021 NetDEF (c), All rights reserved. + * Copyright 2020 6WIND (c), All rights reserved. + */ + +#include + +#include "memory.h" +#include "command.h" +#include "table.h" +#include "vty.h" +#include "log.h" +#include "printfrr.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_opaque.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_gr.h" +#include "ospfd/ospf_errors.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_gr_clippy.c" + +static void ospf_gr_grace_period_expired(struct event *thread); + +/* Lookup self-originated Grace-LSA in the LSDB. */ +static struct ospf_lsa *ospf_gr_lsa_lookup(struct ospf *ospf, + struct ospf_area *area) +{ + struct ospf_lsa *lsa; + struct in_addr lsa_id; + uint32_t lsa_id_host_byte_order; + + lsa_id_host_byte_order = SET_OPAQUE_LSID(OPAQUE_TYPE_GRACE_LSA, 0); + lsa_id.s_addr = htonl(lsa_id_host_byte_order); + lsa = ospf_lsa_lookup(ospf, area, OSPF_OPAQUE_LINK_LSA, lsa_id, + ospf->router_id); + + return lsa; +} + +/* Fill in fields of the Grace-LSA that is being originated. */ +static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info, + struct ospf_interface *oi, + enum ospf_gr_restart_reason reason, + struct stream *s) +{ + struct grace_tlv_graceperiod tlv_period = {}; + struct grace_tlv_restart_reason tlv_reason = {}; + struct grace_tlv_restart_addr tlv_address = {}; + + /* Put grace period. */ + tlv_period.header.type = htons(GRACE_PERIOD_TYPE); + tlv_period.header.length = htons(GRACE_PERIOD_LENGTH); + tlv_period.interval = htonl(gr_info->grace_period); + stream_put(s, &tlv_period, sizeof(tlv_period)); + + /* Put restart reason. */ + tlv_reason.header.type = htons(RESTART_REASON_TYPE); + tlv_reason.header.length = htons(RESTART_REASON_LENGTH); + tlv_reason.reason = reason; + stream_put(s, &tlv_reason, sizeof(tlv_reason)); + + /* Put IP address. */ + if (oi->type == OSPF_IFTYPE_BROADCAST || oi->type == OSPF_IFTYPE_NBMA + || oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) { + tlv_address.header.type = htons(RESTARTER_IP_ADDR_TYPE); + tlv_address.header.length = htons(RESTARTER_IP_ADDR_LEN); + tlv_address.addr = oi->address->u.prefix4; + stream_put(s, &tlv_address, sizeof(tlv_address)); + } +} + +/* Generate Grace-LSA for a given interface. */ +static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi, + enum ospf_gr_restart_reason reason) +{ + struct stream *s; + struct lsa_header *lsah; + struct ospf_lsa *new; + uint8_t options, lsa_type; + struct in_addr lsa_id; + uint32_t lsa_id_host_byte_order; + uint16_t length; + + /* Create a stream for LSA. */ + s = stream_new(OSPF_MAX_LSA_SIZE); + + lsah = (struct lsa_header *)STREAM_DATA(s); + + options = LSA_OPTIONS_GET(oi->area); + options |= LSA_OPTIONS_NSSA_GET(oi->area); + options |= OSPF_OPTION_O; + + lsa_type = OSPF_OPAQUE_LINK_LSA; + lsa_id_host_byte_order = SET_OPAQUE_LSID(OPAQUE_TYPE_GRACE_LSA, 0); + lsa_id.s_addr = htonl(lsa_id_host_byte_order); + + /* Set opaque-LSA header fields. */ + lsa_header_set(s, options, lsa_type, lsa_id, oi->ospf->router_id); + + /* Set opaque-LSA body fields. */ + ospf_gr_lsa_body_set(&oi->ospf->gr_info, oi, reason, s); + + /* Set length. */ + length = stream_get_endp(s); + lsah->length = htons(length); + + /* Now, create an OSPF LSA instance. */ + new = ospf_lsa_new_and_data(length); + + if (IS_DEBUG_OSPF_GR) + zlog_debug("LSA[Type%d:%pI4]: Create an Opaque-LSA/GR instance", + lsa_type, &lsa_id); + + new->area = oi->area; + new->oi = oi; + SET_FLAG(new->flags, OSPF_LSA_SELF); + memcpy(new->data, lsah, length); + stream_free(s); + + return new; +} + +/* Originate and install Grace-LSA for a given interface. */ +static void ospf_gr_lsa_originate(struct ospf_interface *oi, + enum ospf_gr_restart_reason reason, + bool maxage) +{ + struct ospf_lsa *lsa, *old; + + /* Skip originating a Grace-LSA when not necessary. */ + if (!if_is_operative(oi->ifp) || if_is_loopback(oi->ifp) || + (reason != OSPF_GR_UNKNOWN_RESTART && + ospf_interface_neighbor_count(oi) == 0)) + return; + + /* Create new Grace-LSA. */ + lsa = ospf_gr_lsa_new(oi, reason); + if (!lsa) { + zlog_warn("%s: ospf_gr_lsa_new() failed", __func__); + return; + } + + if (maxage) + lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + + /* Find the old LSA and increase the seqno. */ + old = ospf_gr_lsa_lookup(oi->ospf, oi->area); + if (old) + lsa->data->ls_seqnum = lsa_seqnum_increment(old); + + if (!maxage && reason == OSPF_GR_UNKNOWN_RESTART) { + struct list *update; + struct in_addr addr; + + /* + * When performing an unplanned restart, send a handcrafted + * Grace-LSA since the interface isn't fully initialized yet. + */ + ospf_lsa_checksum(lsa->data); + ospf_lsa_lock(lsa); + update = list_new(); + listnode_add(update, lsa); + addr.s_addr = htonl(OSPF_ALLSPFROUTERS); + ospf_ls_upd_queue_send(oi, update, addr, true); + list_delete(&update); + ospf_lsa_discard(lsa); + } else { + /* Install this LSA into LSDB. */ + if (ospf_lsa_install(oi->ospf, oi, lsa) == NULL) { + zlog_warn("%s: ospf_lsa_install() failed", __func__); + ospf_lsa_unlock(&lsa); + return; + } + + /* Flood the LSA through out the interface */ + ospf_flood_through_interface(oi, NULL, lsa); + } + + /* Update new LSA origination count. */ + oi->ospf->lsa_originate_count++; +} + +/* Flush all self-originated Grace-LSAs. */ +static void ospf_gr_flush_grace_lsas(struct ospf *ospf) +{ + struct ospf_area *area; + struct listnode *anode; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, anode, area)) { + struct ospf_interface *oi; + struct listnode *inode; + + for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "GR: flushing self-originated Grace-LSA [area %pI4] [interface %s]", + &area->area_id, oi->ifp->name); + + ospf_gr_lsa_originate(oi, ospf->gr_info.reason, true); + } + } +} + +/* Exit from the Graceful Restart mode. */ +static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason) +{ + struct ospf_area *area; + struct listnode *onode, *anode; + + if (IS_DEBUG_OSPF_GR) + zlog_debug("GR: exiting graceful restart: %s", reason); + + ospf->gr_info.restart_in_progress = false; + EVENT_OFF(ospf->gr_info.t_grace_period); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, onode, area)) { + struct ospf_interface *oi; + + /* + * 1) The router should reoriginate its router-LSAs for all + * attached areas in order to make sure they have the correct + * contents. + */ + ospf_router_lsa_update_area(area); + + for (ALL_LIST_ELEMENTS_RO(area->oiflist, anode, oi)) { + /* Disable hello delay. */ + if (oi->gr.hello_delay.t_grace_send) { + oi->gr.hello_delay.elapsed_seconds = 0; + EVENT_OFF(oi->gr.hello_delay.t_grace_send); + OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, + ospf_hello_timer, 1); + } + + /* + * 2) The router should reoriginate network-LSAs on all + * segments where it is the Designated Router. + */ + if (oi->state == ISM_DR) + ospf_network_lsa_update(oi); + } + } + + /* + * 5) Any received self-originated LSAs that are no longer valid should + * be flushed. + */ + ospf_schedule_abr_task(ospf); + + /* + * 3) The router reruns its OSPF routing calculations, this time + * installing the results into the system forwarding table, and + * originating summary-LSAs, Type-7 LSAs and AS-external-LSAs as + * necessary. + * + * 4) Any remnant entries in the system forwarding table that were + * installed before the restart, but that are no longer valid, + * should be removed. + */ + ospf->gr_info.finishing_restart = true; + XFREE(MTYPE_TMP, ospf->gr_info.exit_reason); + ospf->gr_info.exit_reason = XSTRDUP(MTYPE_TMP, reason); + ospf_spf_calculate_schedule(ospf, SPF_FLAG_GR_FINISH); + + /* 6) Any grace-LSAs that the router originated should be flushed. */ + ospf_gr_flush_grace_lsas(ospf); +} + +/* Enter the Graceful Restart mode. */ +void ospf_gr_restart_enter(struct ospf *ospf, + enum ospf_gr_restart_reason reason, time_t timestamp) +{ + unsigned long remaining_time; + + ospf->gr_info.restart_in_progress = true; + ospf->gr_info.reason = reason; + + /* Schedule grace period timeout. */ + remaining_time = timestamp - time(NULL); + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "GR: remaining time until grace period expires: %lu(s)", + remaining_time); + + event_add_timer(master, ospf_gr_grace_period_expired, ospf, + remaining_time, &ospf->gr_info.t_grace_period); +} + +/* Check if a Router-LSA contains a given link. */ +static bool ospf_router_lsa_contains_adj(struct ospf_lsa *lsa, + struct in_addr *id) +{ + struct router_lsa *rl; + + rl = (struct router_lsa *)lsa->data; + for (int i = 0; i < ntohs(rl->links); i++) { + struct in_addr *link_id = &rl->link[i].link_id; + + if (rl->link[i].type != LSA_LINK_TYPE_POINTOPOINT) + continue; + + if (IPV4_ADDR_SAME(id, link_id)) + return true; + } + + return false; +} + +static bool ospf_gr_check_router_lsa_consistency(struct ospf *ospf, + struct ospf_area *area, + struct ospf_lsa *lsa) +{ + if (CHECK_FLAG(lsa->flags, OSPF_LSA_SELF)) { + struct ospf_lsa *lsa_self = lsa; + struct router_lsa *rl = (struct router_lsa *)lsa->data; + + for (int i = 0; i < ntohs(rl->links); i++) { + struct in_addr *link_id = &rl->link[i].link_id; + struct ospf_lsa *lsa_adj; + + if (rl->link[i].type != LSA_LINK_TYPE_POINTOPOINT) + continue; + + lsa_adj = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA, + *link_id); + if (!lsa_adj) + continue; + + if (!ospf_router_lsa_contains_adj(lsa_adj, + &lsa_self->data->id)) + return false; + } + } else { + struct ospf_lsa *lsa_self; + + lsa_self = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA, + ospf->router_id); + if (!lsa_self + || !CHECK_FLAG(lsa_self->flags, OSPF_LSA_RECEIVED)) + return true; + + if (ospf_router_lsa_contains_adj(lsa, &ospf->router_id) + != ospf_router_lsa_contains_adj(lsa_self, &lsa->data->id)) + return false; + } + + return true; +} + +/* + * Check for LSAs that are inconsistent with the pre-restart LSAs, and abort the + * ongoing graceful restart when that's the case. + */ +void ospf_gr_check_lsdb_consistency(struct ospf *ospf, struct ospf_area *area) +{ + struct route_node *rn; + struct ospf_lsa *lsa; + + for (rn = route_top(ROUTER_LSDB(area)); rn; rn = route_next(rn)) { + lsa = rn->info; + if (!lsa) + continue; + + if (!ospf_gr_check_router_lsa_consistency(ospf, area, lsa)) { + char reason[256]; + + snprintfrr(reason, sizeof(reason), + "detected inconsistent LSA[%s] [area %pI4]", + dump_lsa_key(lsa), &area->area_id); + ospf_gr_restart_exit(ospf, reason); + route_unlock_node(rn); + return; + } + } +} + +/* Lookup neighbor by address in a given OSPF area. */ +static struct ospf_neighbor * +ospf_area_nbr_lookup_by_addr(struct ospf_area *area, struct in_addr *addr) +{ + struct ospf_interface *oi; + struct ospf_neighbor *nbr; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(area->oiflist, node, oi)) { + nbr = ospf_nbr_lookup_by_addr(oi->nbrs, addr); + if (nbr) + return nbr; + } + + return NULL; +} + +/* Lookup neighbor by Router ID in a given OSPF area. */ +static struct ospf_neighbor * +ospf_area_nbr_lookup_by_routerid(struct ospf_area *area, struct in_addr *id) +{ + struct ospf_interface *oi; + struct ospf_neighbor *nbr; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(area->oiflist, node, oi)) { + nbr = ospf_nbr_lookup_by_routerid(oi->nbrs, id); + if (nbr) + return nbr; + } + + return NULL; +} + +/* Check if there's a fully formed adjacency with the given neighbor ID. */ +static bool ospf_gr_check_adj_id(struct ospf_area *area, + struct in_addr *nbr_id) +{ + struct ospf_neighbor *nbr; + + nbr = ospf_area_nbr_lookup_by_routerid(area, nbr_id); + if (!nbr || nbr->state < NSM_Full) { + if (IS_DEBUG_OSPF_GR) + zlog_debug("GR: missing adjacency to router %pI4", + nbr_id); + return false; + } + + return true; +} + +static bool ospf_gr_check_adjs_lsa_transit(struct ospf_area *area, + struct in_addr *link_id) +{ + struct ospf *ospf = area->ospf; + struct ospf_interface *oi; + + /* + * Check if the transit network refers to a local interface (in which + * case it must be a DR for that network). + */ + oi = ospf_if_lookup_by_local_addr(ospf, NULL, *link_id); + if (oi) { + struct ospf_lsa *lsa; + struct network_lsa *nlsa; + size_t cnt; + + /* Lookup Network LSA corresponding to this interface. */ + lsa = ospf_lsa_lookup_by_id(area, OSPF_NETWORK_LSA, *link_id); + if (!lsa) + return false; + + /* Iterate over all routers present in the network. */ + nlsa = (struct network_lsa *)lsa->data; + cnt = (lsa->size - (OSPF_LSA_HEADER_SIZE + 4)) / 4; + for (size_t i = 0; i < cnt; i++) { + struct in_addr *nbr_id = &nlsa->routers[i]; + + /* Skip self in the pseudonode. */ + if (IPV4_ADDR_SAME(nbr_id, &ospf->router_id)) + continue; + + /* + * Check if there's a fully formed adjacency with this + * router. + */ + if (!ospf_gr_check_adj_id(area, nbr_id)) + return false; + } + } else { + struct ospf_neighbor *nbr; + + /* Check if there's a fully formed adjacency with the DR. */ + nbr = ospf_area_nbr_lookup_by_addr(area, link_id); + if (!nbr || nbr->state < NSM_Full) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "GR: missing adjacency to DR router %pI4", + link_id); + return false; + } + } + + return true; +} + +static bool ospf_gr_check_adjs_lsa(struct ospf_area *area, struct ospf_lsa *lsa) +{ + struct router_lsa *rl = (struct router_lsa *)lsa->data; + + for (int i = 0; i < ntohs(rl->links); i++) { + struct in_addr *link_id = &rl->link[i].link_id; + + switch (rl->link[i].type) { + case LSA_LINK_TYPE_POINTOPOINT: + if (!ospf_gr_check_adj_id(area, link_id)) + return false; + break; + case LSA_LINK_TYPE_TRANSIT: + if (!ospf_gr_check_adjs_lsa_transit(area, link_id)) + return false; + break; + default: + break; + } + } + + return true; +} + +/* + * Check if all adjacencies prior to the restart were reestablished. + * + * This is done using pre-restart Router LSAs and pre-restart Network LSAs + * received from the helping neighbors. + */ +void ospf_gr_check_adjs(struct ospf *ospf) +{ + struct ospf_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + struct ospf_lsa *lsa_self; + + lsa_self = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA, + ospf->router_id); + if (!lsa_self || !ospf_gr_check_adjs_lsa(area, lsa_self)) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "GR: not all adjacencies were reestablished yet [area %pI4]", + &area->area_id); + return; + } + } + + ospf_gr_restart_exit(ospf, "all adjacencies were reestablished"); +} + +/* Handling of grace period expiry. */ +static void ospf_gr_grace_period_expired(struct event *thread) +{ + struct ospf *ospf = EVENT_ARG(thread); + + ospf->gr_info.t_grace_period = NULL; + ospf_gr_restart_exit(ospf, "grace period has expired"); +} + +/* + * Returns the path of the file (non-volatile memory) that contains GR status + * information. + */ +static char *ospf_gr_nvm_filepath(struct ospf *ospf) +{ + static char filepath[MAXPATHLEN]; + char instance[16] = ""; + + if (ospf->instance) + snprintf(instance, sizeof(instance), "-%d", ospf->instance); + snprintf(filepath, sizeof(filepath), OSPFD_GR_STATE, instance); + return filepath; +} + +/* Send extra Grace-LSA out the interface (unplanned outages only). */ +void ospf_gr_iface_send_grace_lsa(struct event *thread) +{ + struct ospf_interface *oi = EVENT_ARG(thread); + struct ospf_if_params *params = IF_DEF_PARAMS(oi->ifp); + + ospf_gr_lsa_originate(oi, oi->ospf->gr_info.reason, false); + + if (++oi->gr.hello_delay.elapsed_seconds < params->v_gr_hello_delay) + event_add_timer(master, ospf_gr_iface_send_grace_lsa, oi, 1, + &oi->gr.hello_delay.t_grace_send); + else + OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, ospf_hello_timer, 1); +} + +/* + * Record in non-volatile memory that the given OSPF instance is attempting to + * perform a graceful restart. + */ +static void ospf_gr_nvm_update(struct ospf *ospf, bool prepare) +{ + char *filepath; + const char *inst_name; + json_object *json; + json_object *json_instances; + json_object *json_instance; + + filepath = ospf_gr_nvm_filepath(ospf); + inst_name = ospf_get_name(ospf); + + json = json_object_from_file(filepath); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_get_ex(json_instances, inst_name, &json_instance); + if (!json_instance) { + json_instance = json_object_new_object(); + json_object_object_add(json_instances, inst_name, + json_instance); + } + + json_object_int_add(json_instance, "gracePeriod", + ospf->gr_info.grace_period); + + /* + * Record not only the grace period, but also a UNIX timestamp + * corresponding to the end of that period. That way, once ospfd is + * restarted, it will be possible to take into account the time that + * passed while ospfd wasn't running. + */ + if (prepare) + json_object_int_add(json_instance, "timestamp", + time(NULL) + ospf->gr_info.grace_period); + + json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + +/* + * Delete GR status information about the given OSPF instance from non-volatile + * memory. + */ +void ospf_gr_nvm_delete(struct ospf *ospf) +{ + char *filepath; + const char *inst_name; + json_object *json; + json_object *json_instances; + + filepath = ospf_gr_nvm_filepath(ospf); + inst_name = ospf_get_name(ospf); + + json = json_object_from_file(filepath); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_del(json_instances, inst_name); + + json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + +/* + * Fetch from non-volatile memory whether the given OSPF instance is performing + * a graceful shutdown or not. + */ +void ospf_gr_nvm_read(struct ospf *ospf) +{ + char *filepath; + const char *inst_name; + json_object *json; + json_object *json_instances; + json_object *json_instance; + json_object *json_timestamp; + json_object *json_grace_period; + time_t timestamp = 0; + + filepath = ospf_gr_nvm_filepath(ospf); + inst_name = ospf_get_name(ospf); + + json = json_object_from_file(filepath); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_get_ex(json_instances, inst_name, &json_instance); + if (!json_instance) { + json_instance = json_object_new_object(); + json_object_object_add(json_instances, inst_name, + json_instance); + } + + json_object_object_get_ex(json_instance, "gracePeriod", + &json_grace_period); + json_object_object_get_ex(json_instance, "timestamp", &json_timestamp); + + if (json_timestamp) { + time_t now; + + /* Planned GR: check if the grace period has already expired. */ + now = time(NULL); + timestamp = json_object_get_int(json_timestamp); + if (now > timestamp) { + ospf_gr_restart_exit( + ospf, "grace period has expired already"); + } else + ospf_gr_restart_enter(ospf, OSPF_GR_SW_RESTART, + timestamp); + } else if (json_grace_period) { + uint32_t grace_period; + + /* + * Unplanned GR: the Grace-LSAs will be sent later as soon as + * the interfaces are operational. + */ + grace_period = json_object_get_int(json_grace_period); + ospf->gr_info.grace_period = grace_period; + ospf_gr_restart_enter(ospf, OSPF_GR_UNKNOWN_RESTART, + time(NULL) + ospf->gr_info.grace_period); + } + + json_object_object_del(json_instances, inst_name); + + json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + +void ospf_gr_unplanned_start_interface(struct ospf_interface *oi) +{ + /* Send Grace-LSA. */ + ospf_gr_lsa_originate(oi, oi->ospf->gr_info.reason, false); + + /* Start GR hello-delay interval. */ + oi->gr.hello_delay.elapsed_seconds = 0; + event_add_timer(master, ospf_gr_iface_send_grace_lsa, oi, 1, + &oi->gr.hello_delay.t_grace_send); +} + +/* Prepare to start a Graceful Restart. */ +static void ospf_gr_prepare(void) +{ + struct ospf *ospf; + struct ospf_interface *oi; + struct listnode *onode; + + for (ALL_LIST_ELEMENTS_RO(om->ospf, onode, ospf)) { + struct listnode *inode; + + if (!ospf->gr_info.restart_support + || ospf->gr_info.prepare_in_progress) + continue; + + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "GR: preparing to perform a graceful restart [period %u second(s)] [vrf %s]", + ospf->gr_info.grace_period, + ospf_vrf_id_to_name(ospf->vrf_id)); + + if (!CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) { + zlog_warn( + "%s: failed to activate graceful restart: opaque capability not enabled", + __func__); + continue; + } + + /* Send a Grace-LSA to all neighbors. */ + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, inode, oi)) { + if (OSPF_IF_PARAM(oi, opaque_capable)) + ospf_gr_lsa_originate(oi, OSPF_GR_SW_RESTART, + false); + else + zlog_debug( + "GR: skipping grace LSA on interface %s (%s) with opaque capability disabled", + IF_NAME(oi), ospf_get_name(oi->ospf)); + } + + /* Record end of the grace period in non-volatile memory. */ + ospf_gr_nvm_update(ospf, true); + + /* + * Mark that a Graceful Restart preparation is in progress, to + * prevent ospfd from flushing its self-originated LSAs on exit. + */ + ospf->gr_info.prepare_in_progress = true; + } +} + +DEFPY(graceful_restart_prepare, graceful_restart_prepare_cmd, + "graceful-restart prepare ip ospf", + "Graceful Restart commands\n" + "Prepare upcoming graceful restart\n" + IP_STR + "Prepare to restart the OSPF process\n") +{ + struct ospf *ospf; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) { + vty_out(vty, + "%% Can't start graceful restart: opaque capability not enabled (VRF %s)\n\n", + ospf_get_name(ospf)); + return CMD_WARNING; + } + } + + ospf_gr_prepare(); + + return CMD_SUCCESS; +} + +DEFPY(graceful_restart, graceful_restart_cmd, + "graceful-restart [grace-period (1-1800)$grace_period]", + OSPF_GR_STR + "Maximum length of the 'grace period'\n" + "Maximum length of the 'grace period' in seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + /* Check and get restart period if present. */ + if (!grace_period_str) + grace_period = OSPF_DFLT_GRACE_INTERVAL; + + ospf->gr_info.restart_support = true; + ospf->gr_info.grace_period = grace_period; + + /* Freeze OSPF routes in the RIB. */ + (void)ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period); + + /* Record that GR is enabled in non-volatile memory. */ + ospf_gr_nvm_update(ospf, false); + + return CMD_SUCCESS; +} + +DEFPY(no_graceful_restart, no_graceful_restart_cmd, + "no graceful-restart [grace-period (1-1800)]", + NO_STR OSPF_GR_STR + "Maximum length of the 'grace period'\n" + "Maximum length of the 'grace period' in seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + if (!ospf->gr_info.restart_support) + return CMD_SUCCESS; + + if (ospf->gr_info.prepare_in_progress) { + vty_out(vty, + "%% Error: Graceful Restart preparation in progress\n"); + return CMD_WARNING; + } + + ospf->gr_info.restart_support = false; + ospf->gr_info.grace_period = OSPF_DFLT_GRACE_INTERVAL; + ospf_gr_nvm_delete(ospf); + ospf_zebra_gr_disable(ospf); + + return CMD_SUCCESS; +} + +void ospf_gr_init(void) +{ + install_element(ENABLE_NODE, &graceful_restart_prepare_cmd); + install_element(OSPF_NODE, &graceful_restart_cmd); + install_element(OSPF_NODE, &no_graceful_restart_cmd); +} diff --git a/ospfd/ospf_gr.h b/ospfd/ospf_gr.h new file mode 100644 index 0000000..22f9e1e --- /dev/null +++ b/ospfd/ospf_gr.h @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF Graceful Restart helper functions. + * + * Copyright (C) 2020-21 Vmware, Inc. + * Rajesh Kumar Girada + */ + +#ifndef _ZEBRA_OSPF_GR_H +#define _ZEBRA_OSPF_GR_H + +#define OSPF_GR_NOT_HELPER 0 +#define OSPF_GR_ACTIVE_HELPER 1 + +#define OSPF_GR_HELPER_NO_LSACHECK 0 +#define OSPF_GR_HELPER_LSACHECK 1 + +#define OSPF_MAX_GRACE_INTERVAL 1800 +#define OSPF_MIN_GRACE_INTERVAL 1 +#define OSPF_DFLT_GRACE_INTERVAL 120 + +enum ospf_helper_exit_reason { + OSPF_GR_HELPER_EXIT_NONE = 0, + OSPF_GR_HELPER_INPROGRESS, + OSPF_GR_HELPER_TOPO_CHG, + OSPF_GR_HELPER_GRACE_TIMEOUT, + OSPF_GR_HELPER_COMPLETED +}; + +enum ospf_gr_restart_reason { + OSPF_GR_UNKNOWN_RESTART = 0, + OSPF_GR_SW_RESTART = 1, + OSPF_GR_SW_UPGRADE = 2, + OSPF_GR_SWITCH_REDUNDANT_CARD = 3, + OSPF_GR_INVALID_REASON_CODE = 4 +}; + +enum ospf_gr_helper_rejected_reason { + OSPF_HELPER_REJECTED_NONE, + OSPF_HELPER_SUPPORT_DISABLED, + OSPF_HELPER_NOT_A_VALID_NEIGHBOUR, + OSPF_HELPER_PLANNED_ONLY_RESTART, + OSPF_HELPER_TOPO_CHANGE_RTXMT_LIST, + OSPF_HELPER_LSA_AGE_MORE, + OSPF_HELPER_RESTARTING, +}; + +/* Ref RFC3623 appendex-A */ +/* Grace period TLV */ +#define GRACE_PERIOD_TYPE 1 +#define GRACE_PERIOD_LENGTH 4 + +struct grace_tlv_graceperiod { + struct tlv_header header; + uint32_t interval; +}; + +/* Restart reason TLV */ +#define RESTART_REASON_TYPE 2 +#define RESTART_REASON_LENGTH 1 + +struct grace_tlv_restart_reason { + struct tlv_header header; + uint8_t reason; + uint8_t reserved[3]; +}; + +/* Restarter ip address TLV */ +#define RESTARTER_IP_ADDR_TYPE 3 +#define RESTARTER_IP_ADDR_LEN 4 + +struct grace_tlv_restart_addr { + struct tlv_header header; + struct in_addr addr; +}; + +struct ospf_helper_info { + + /* Grace interval received from + * Restarting Router. + */ + uint32_t recvd_grace_period; + + /* Grace interval used for grace + * gracetimer. + */ + uint32_t actual_grace_period; + + /* Grace timer,This Router acts as + * helper until this timer until + * this timer expires. + */ + struct event *t_grace_timer; + + /* Helper status */ + uint32_t gr_helper_status; + + /* Helper exit reason*/ + enum ospf_helper_exit_reason helper_exit_reason; + + /* Planned/Unplanned restart*/ + enum ospf_gr_restart_reason gr_restart_reason; + + /* Helper rejected reason */ + enum ospf_gr_helper_rejected_reason rejected_reason; +}; + +struct advRtr { + struct in_addr advRtrAddr; +}; + +#define OSPF_HELPER_ENABLE_RTR_COUNT(ospf) (ospf->enable_rtr_list->count) + +/* Check for planned restart */ +#define OSPF_GR_IS_PLANNED_RESTART(reason) \ + ((reason == OSPF_GR_SW_RESTART) || (reason == OSPF_GR_SW_UPGRADE)) + +/* Check the router is HELPER for current neighbour */ +#define OSPF_GR_IS_ACTIVE_HELPER(N) \ + ((N)->gr_helper_info.gr_helper_status == OSPF_GR_ACTIVE_HELPER) + +/* Check the LSA is GRACE LSA */ +#define IS_GRACE_LSA(lsa) \ + ((lsa->data->type == OSPF_OPAQUE_LINK_LSA) \ + && (GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)) \ + == OPAQUE_TYPE_GRACE_LSA)) + +/* Check neighbour is in FULL state */ +#define IS_NBR_STATE_FULL(nbr) (nsm_should_adj(nbr) && (nbr->state == NSM_Full)) + +/* Check neighbour is DR_OTHER and state is 2_WAY */ +#define IS_NBR_STATE_2_WAY_WITH_DROTHER(nbr) \ + ((ospf_get_nbr_ism_role(nbr) == ISM_DROther) \ + && (nbr->state == NSM_TwoWay)) + +#define OSPF_GR_FALSE false +#define OSPF_GR_TRUE true + +#define OSPF_GR_SUCCESS 1 +#define OSPF_GR_FAILURE 0 +#define OSPF_GR_INVALID -1 + +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_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, + enum ospf_helper_exit_reason reason); +extern void ospf_process_maxage_grace_lsa(struct ospf *ospf, + struct ospf_lsa *lsa, + struct ospf_neighbor *nbr); +extern void ospf_helper_handle_topo_chg(struct ospf *ospf, + struct ospf_lsa *lsa); +extern void ospf_gr_helper_support_set(struct ospf *ospf, bool support); +extern void ospf_gr_helper_support_set_per_routerid(struct ospf *ospf, + struct in_addr *rid, + bool support); +extern void ospf_gr_helper_lsa_check_set(struct ospf *ospf, bool lsacheck); +extern void ospf_gr_helper_supported_gracetime_set(struct ospf *ospf, + uint32_t interval); +extern void ospf_gr_helper_set_supported_planned_only_restart(struct ospf *ospf, + bool planned_only); +extern void ospf_gr_iface_send_grace_lsa(struct event *thread); +extern void ospf_gr_restart_enter(struct ospf *ospf, + enum ospf_gr_restart_reason reason, + time_t timestamp); +extern void ospf_gr_check_lsdb_consistency(struct ospf *ospf, + struct ospf_area *area); +extern void ospf_gr_check_adjs(struct ospf *ospf); +extern void ospf_gr_nvm_read(struct ospf *ospf); +extern void ospf_gr_nvm_delete(struct ospf *ospf); +extern void ospf_gr_unplanned_start_interface(struct ospf_interface *oi); +extern void ospf_gr_init(void); + +#endif /* _ZEBRA_OSPF_GR_H */ diff --git a/ospfd/ospf_gr_helper.c b/ospfd/ospf_gr_helper.c new file mode 100644 index 0000000..b97b680 --- /dev/null +++ b/ospfd/ospf_gr_helper.c @@ -0,0 +1,1125 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF Graceful Restart helper functions. + * + * Copyright (C) 2020-21 Vmware, Inc. + * Rajesh Kumar Girada + */ + +#include + +#include "frrevent.h" +#include "memory.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "vty.h" +#include "filter.h" +#include "log.h" +#include "jhash.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_errors.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_gr.h" + +static const char * const ospf_exit_reason_desc[] = { + "Unknown reason", + "Helper in progress", + "Topology Change", + "Grace timer expiry", + "Successful graceful restart", +}; + +static const char * const ospf_restart_reason_desc[] = { + "Unknown restart", + "Software restart", + "Software reload/upgrade", + "Switch to redundant control processor", +}; + +static const char * const ospf_rejected_reason_desc[] = { + "Unknown reason", + "Helper support disabled", + "Neighbour is not in FULL state", + "Supports only planned restart but received unplanned", + "Topo change due to change in lsa rxmt list", + "LSA age is more than Grace interval", + "Router is in the process of graceful restart", +}; + +static void show_ospf_grace_lsa_info(struct vty *vty, struct json_object *json, + struct ospf_lsa *lsa); +static bool ospf_check_change_in_rxmt_list(struct ospf_neighbor *nbr); + +static unsigned int ospf_enable_rtr_hash_key(const void *data) +{ + const struct advRtr *rtr = data; + + return jhash_1word(rtr->advRtrAddr.s_addr, 0); +} + +static bool ospf_enable_rtr_hash_cmp(const void *d1, const void *d2) +{ + const struct advRtr *rtr1 = (struct advRtr *)d1; + const struct advRtr *rtr2 = (struct advRtr *)d2; + + return (rtr1->advRtrAddr.s_addr == rtr2->advRtrAddr.s_addr); +} + +static void *ospf_enable_rtr_hash_alloc(void *p) +{ + struct advRtr *rid; + + rid = XCALLOC(MTYPE_OSPF_GR_HELPER, sizeof(struct advRtr)); + rid->advRtrAddr.s_addr = ((struct in_addr *)p)->s_addr; + + return rid; +} + +static void ospf_disable_rtr_hash_free(void *rtr) +{ + XFREE(MTYPE_OSPF_GR_HELPER, rtr); +} + +static void ospf_enable_rtr_hash_destroy(struct ospf *ospf) +{ + if (ospf->enable_rtr_list == NULL) + return; + + hash_clean_and_free(&ospf->enable_rtr_list, ospf_disable_rtr_hash_free); +} + +/* + * GR exit reason strings + */ +const char *ospf_exit_reason2str(unsigned int reason) +{ + if (reason < array_size(ospf_exit_reason_desc)) + return(ospf_exit_reason_desc[reason]); + else + return "Invalid reason"; +} + +/* + * GR restart reason strings + */ +const char *ospf_restart_reason2str(unsigned int reason) +{ + if (reason < array_size(ospf_restart_reason_desc)) + return(ospf_restart_reason_desc[reason]); + else + return "Invalid reason"; +} + +/* + * GR rejected reason strings + */ +const char *ospf_rejected_reason2str(unsigned int reason) +{ + if (reason < array_size(ospf_rejected_reason_desc)) + return(ospf_rejected_reason_desc[reason]); + else + return "Invalid reason"; +} + +/* + * Initialize GR helper config data structures. + * + * OSPF + * OSPF pointer + * + * Returns: + * Nothing + */ +void ospf_gr_helper_instance_init(struct ospf *ospf) +{ + if (IS_DEBUG_OSPF_GR) + zlog_debug("%s, GR Helper init.", __func__); + + ospf->is_helper_supported = OSPF_GR_FALSE; + ospf->strict_lsa_check = OSPF_GR_TRUE; + ospf->only_planned_restart = OSPF_GR_FALSE; + ospf->supported_grace_time = OSPF_MAX_GRACE_INTERVAL; + ospf->last_exit_reason = OSPF_GR_HELPER_EXIT_NONE; + ospf->active_restarter_cnt = 0; + + 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) + 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) + zlog_debug("%s, GR Helper init.", __func__); + + rc = ospf_register_opaque_functab( + OSPF_OPAQUE_LINK_LSA, OPAQUE_TYPE_GRACE_LSA, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, show_ospf_grace_lsa_info, NULL, NULL, + NULL, NULL); + if (rc != 0) { + flog_warn(EC_OSPF_OPAQUE_REGISTRATION, + "%s: Failed to register Grace LSA functions", + __func__); + } +} + +/* + * De-Initialize GR helper config data structures. + * + * Returns: + * Nothing + */ +void ospf_gr_helper_stop(void) +{ + if (IS_DEBUG_OSPF_GR) + zlog_debug("%s, GR helper deinit.", __func__); + + ospf_delete_opaque_functab(OSPF_OPAQUE_LINK_LSA, OPAQUE_TYPE_GRACE_LSA); +} + +/* + * Extracting tlv info from GRACE LSA. + * + * lsa + * ospf grace lsa + * + * Returns: + * interval : grace interval. + * addr : RESTARTER address. + * reason : Restarting reason. + */ +static int ospf_extract_grace_lsa_fields(struct ospf_lsa *lsa, + uint32_t *interval, + struct in_addr *addr, uint8_t *reason) +{ + struct lsa_header *lsah = NULL; + struct tlv_header *tlvh = NULL; + struct grace_tlv_graceperiod *grace_period; + struct grace_tlv_restart_reason *gr_reason; + struct grace_tlv_restart_addr *restart_addr; + uint16_t length = 0; + int sum = 0; + + lsah = (struct lsa_header *)lsa->data; + + /* Check LSA len */ + if (lsa->size <= OSPF_LSA_HEADER_SIZE) { + if (IS_DEBUG_OSPF_GR) + zlog_debug("%s: Malformed packet: Invalid LSA len:%d", + __func__, length); + return OSPF_GR_FAILURE; + } + + length = lsa->size - OSPF_LSA_HEADER_SIZE; + + for (tlvh = TLV_HDR_TOP(lsah); sum < length && tlvh; + tlvh = TLV_HDR_NEXT(tlvh)) { + + /* Check TLV len against overall LSA */ + if (sum + TLV_SIZE(tlvh) > length) { + if (IS_DEBUG_OSPF_GR) + zlog_debug("%s: Malformed packet: Invalid TLV len:%u", + __func__, TLV_SIZE(tlvh)); + return OSPF_GR_FAILURE; + } + + switch (ntohs(tlvh->type)) { + case GRACE_PERIOD_TYPE: + if (TLV_SIZE(tlvh) < + sizeof(struct grace_tlv_graceperiod)) { + zlog_debug("%s: Malformed packet: Invalid grace TLV len:%u", + __func__, TLV_SIZE(tlvh)); + return OSPF_GR_FAILURE; + } + + grace_period = (struct grace_tlv_graceperiod *)tlvh; + *interval = ntohl(grace_period->interval); + sum += TLV_SIZE(tlvh); + + /* Check if grace interval is valid */ + if (*interval > OSPF_MAX_GRACE_INTERVAL + || *interval < OSPF_MIN_GRACE_INTERVAL) + return OSPF_GR_FAILURE; + break; + case RESTART_REASON_TYPE: + if (TLV_SIZE(tlvh) < + sizeof(struct grace_tlv_restart_reason)) { + zlog_debug("%s: Malformed packet: Invalid reason TLV len:%u", + __func__, TLV_SIZE(tlvh)); + return OSPF_GR_FAILURE; + } + + gr_reason = (struct grace_tlv_restart_reason *)tlvh; + *reason = gr_reason->reason; + sum += TLV_SIZE(tlvh); + + if (*reason >= OSPF_GR_INVALID_REASON_CODE) + return OSPF_GR_FAILURE; + break; + case RESTARTER_IP_ADDR_TYPE: + if (TLV_SIZE(tlvh) < + sizeof(struct grace_tlv_restart_addr)) { + zlog_debug("%s: Malformed packet: Invalid addr TLV len:%u", + __func__, TLV_SIZE(tlvh)); + return OSPF_GR_FAILURE; + } + + restart_addr = (struct grace_tlv_restart_addr *)tlvh; + addr->s_addr = restart_addr->addr.s_addr; + sum += TLV_SIZE(tlvh); + break; + default: + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, Malformed packet.Invalid TLV type:%d", + __func__, ntohs(tlvh->type)); + return OSPF_GR_FAILURE; + } + } + + return OSPF_GR_SUCCESS; +} + +/* + * Grace timer expiry handler. + * HELPER aborts its role at grace timer expiry. + * + * thread + * thread pointer + * + * Returns: + * Nothing + */ +static void ospf_handle_grace_timer_expiry(struct event *thread) +{ + struct ospf_neighbor *nbr = EVENT_ARG(thread); + + nbr->gr_helper_info.t_grace_timer = NULL; + + ospf_gr_helper_exit(nbr, OSPF_GR_HELPER_GRACE_TIMEOUT); +} + +/* + * Process Grace LSA.If it is eligible move to HELPER role. + * Ref rfc3623 section 3.1 + * + * ospf + * OSPF pointer. + * + * lsa + * Grace LSA received from RESTARTER. + * + * nbr + * OSPF neighbour which requests the router to act as + * HELPER. + * + * Returns: + * status. + * If supported as HELPER : OSPF_GR_HELPER_INPROGRESS + * If Not supported as HELPER : OSPF_GR_HELPER_NONE + */ +int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa, + struct ospf_neighbor *nbr) +{ + struct in_addr restart_addr = {0}; + uint8_t restart_reason = 0; + uint32_t grace_interval = 0; + uint32_t actual_grace_interval = 0; + struct advRtr lookup; + struct ospf_neighbor *restarter = NULL; + struct ospf_interface *oi = nbr->oi; + int ret; + + + /* Extract the grace lsa packet fields */ + ret = ospf_extract_grace_lsa_fields(lsa, &grace_interval, &restart_addr, + &restart_reason); + if (ret != OSPF_GR_SUCCESS) { + if (IS_DEBUG_OSPF_GR) + zlog_debug("%s, Wrong Grace LSA packet.", __func__); + return OSPF_GR_NOT_HELPER; + } + + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, Grace LSA received from %pI4, grace interval:%u, restart reason:%s", + __func__, &restart_addr, grace_interval, + ospf_restart_reason2str(restart_reason)); + + /* 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. + */ + if (oi->type != OSPF_IFTYPE_POINTOPOINT) { + restarter = ospf_nbr_lookup_by_addr(oi->nbrs, &restart_addr); + + if (!restarter) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, Restarter is not a nbr(%pI4) for this router.", + __func__, &restart_addr); + return OSPF_GR_NOT_HELPER; + } + } else + restarter = nbr; + + /* Verify Helper enabled globally */ + if (!ospf->is_helper_supported) { + /* Verify that Helper support is enabled for the + * current neighbour router-id. + */ + lookup.advRtrAddr.s_addr = restarter->router_id.s_addr; + + if (!hash_lookup(ospf->enable_rtr_list, &lookup)) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, HELPER support is disabled, So not a HELPER", + __func__); + restarter->gr_helper_info.rejected_reason = + OSPF_HELPER_SUPPORT_DISABLED; + return OSPF_GR_NOT_HELPER; + } + } + + + /* Check neighbour is in FULL state and + * became a adjacency. + */ + if (!IS_NBR_STATE_FULL(restarter)) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, This Neighbour %pI4 is not in FULL state.", + __func__, &restarter->src); + restarter->gr_helper_info.rejected_reason = + OSPF_HELPER_NOT_A_VALID_NEIGHBOUR; + return OSPF_GR_NOT_HELPER; + } + + /* Based on the restart reason from grace lsa + * check the current router is supporting or not + */ + if (ospf->only_planned_restart + && !OSPF_GR_IS_PLANNED_RESTART(restart_reason)) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, Router supports only planned restarts but received the GRACE LSA for an unplanned restart.", + __func__); + restarter->gr_helper_info.rejected_reason = + OSPF_HELPER_PLANNED_ONLY_RESTART; + return OSPF_GR_NOT_HELPER; + } + + /* Check the retransmission list of this + * neighbour, check any change in lsas. + */ + if (ospf->strict_lsa_check && !ospf_ls_retransmit_isempty(restarter) + && ospf_check_change_in_rxmt_list(restarter)) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, Changed LSA in Rxmt list. So not Helper.", + __func__); + restarter->gr_helper_info.rejected_reason = + OSPF_HELPER_TOPO_CHANGE_RTXMT_LIST; + return OSPF_GR_NOT_HELPER; + } + + /*LSA age must be less than the grace period */ + if (ntohs(lsa->data->ls_age) >= grace_interval) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%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; + return OSPF_GR_NOT_HELPER; + } + + if (ospf->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s: router is in the process of graceful restart", + __func__); + restarter->gr_helper_info.rejected_reason = + OSPF_HELPER_RESTARTING; + return OSPF_GR_NOT_HELPER; + } + + /* check supported grace period configured + * if configured, use this to start the grace + * timer otherwise use the interval received + * in grace LSA packet. + */ + actual_grace_interval = grace_interval; + if (grace_interval > ospf->supported_grace_time) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, Received grace period %d is larger than supported grace %d", + __func__, grace_interval, + ospf->supported_grace_time); + actual_grace_interval = ospf->supported_grace_time; + } + + if (OSPF_GR_IS_ACTIVE_HELPER(restarter)) { + if (restarter->gr_helper_info.t_grace_timer) + EVENT_OFF(restarter->gr_helper_info.t_grace_timer); + + if (ospf->active_restarter_cnt > 0) + ospf->active_restarter_cnt--; + + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, Router is already acting as a HELPER for this nbr,so restart the grace timer", + __func__); + } else { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, This Router becomes a HELPER for the neighbour %pI4", + __func__, &restarter->src); + } + + /* Became a Helper to the RESTART neighbour. + * Change the helper status. + */ + restarter->gr_helper_info.gr_helper_status = OSPF_GR_ACTIVE_HELPER; + restarter->gr_helper_info.recvd_grace_period = grace_interval; + restarter->gr_helper_info.actual_grace_period = actual_grace_interval; + restarter->gr_helper_info.gr_restart_reason = restart_reason; + restarter->gr_helper_info.rejected_reason = OSPF_HELPER_REJECTED_NONE; + + /* Increment the active restarter count */ + ospf->active_restarter_cnt++; + + if (IS_DEBUG_OSPF_GR) + zlog_debug("%s, Grace timer started.interval:%d", __func__, + actual_grace_interval); + + /* Start the grace timer */ + event_add_timer(master, ospf_handle_grace_timer_expiry, restarter, + actual_grace_interval, + &restarter->gr_helper_info.t_grace_timer); + + return OSPF_GR_ACTIVE_HELPER; +} + +/* + * API to check any change in the neighbor's + * retransmission list. + * + * nbr + * OSPF neighbor + * + * Returns: + * TRUE - if any change in the lsa. + * FALSE - no change in the lsas. + */ +static bool ospf_check_change_in_rxmt_list(struct ospf_neighbor *nbr) +{ + struct route_node *rn; + struct ospf_lsa *lsa; + struct route_table *tbl; + + tbl = nbr->ls_rxmt.type[OSPF_ROUTER_LSA].db; + LSDB_LOOP (tbl, rn, lsa) + if (lsa->to_be_acknowledged) + return OSPF_GR_TRUE; + tbl = nbr->ls_rxmt.type[OSPF_NETWORK_LSA].db; + LSDB_LOOP (tbl, rn, lsa) + if (lsa->to_be_acknowledged) + return OSPF_GR_TRUE; + + tbl = nbr->ls_rxmt.type[OSPF_SUMMARY_LSA].db; + LSDB_LOOP (tbl, rn, lsa) + if (lsa->to_be_acknowledged) + return OSPF_GR_TRUE; + + tbl = nbr->ls_rxmt.type[OSPF_ASBR_SUMMARY_LSA].db; + LSDB_LOOP (tbl, rn, lsa) + if (lsa->to_be_acknowledged) + return OSPF_GR_TRUE; + + tbl = nbr->ls_rxmt.type[OSPF_AS_EXTERNAL_LSA].db; + LSDB_LOOP (tbl, rn, lsa) + if (lsa->to_be_acknowledged) + return OSPF_GR_TRUE; + + tbl = nbr->ls_rxmt.type[OSPF_AS_NSSA_LSA].db; + LSDB_LOOP (tbl, rn, lsa) + if (lsa->to_be_acknowledged) + return OSPF_GR_TRUE; + + return OSPF_GR_FALSE; +} + +/* + * Actions to be taken when topo change detected + * HELPER will exit upon topo change. + * + * ospf + * ospf pointer + * lsa + * topo change occurred due to this lsa type (1 to 5 and 7) + * + * Returns: + * Nothing + */ +void ospf_helper_handle_topo_chg(struct ospf *ospf, struct ospf_lsa *lsa) +{ + struct listnode *node; + struct ospf_interface *oi; + + /* Topo change not required to be handled if strict + * LSA check is disabled for this router. + */ + if (!ospf->strict_lsa_check) + return; + + if (IS_DEBUG_OSPF_GR) + zlog_debug("%s: Topo change detected due to LSA[%s]", __func__, + dump_lsa_key(lsa)); + + lsa->to_be_acknowledged = OSPF_GR_TRUE; + + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + struct route_node *rn = NULL; + + if (ospf_interface_neighbor_count(oi) == 0) + continue; + + /* Ref rfc3623 section 3.2.3.b + * If change due to external LSA and if the area is + * stub, then it is not a topo change. Since Type-5 + * lsas will not be flooded in stub area. + */ + if ((oi->area->external_routing == OSPF_AREA_STUB) + && (lsa->data->type == OSPF_AS_EXTERNAL_LSA)) { + continue; + } + + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + struct ospf_neighbor *nbr = NULL; + + if (!rn->info) + continue; + + nbr = rn->info; + + if (OSPF_GR_IS_ACTIVE_HELPER(nbr)) + ospf_gr_helper_exit(nbr, + OSPF_GR_HELPER_TOPO_CHG); + } + } +} + +/* + * Api to exit from HELPER role to take all actions + * required at exit. + * Ref rfc3623 section 3.2 + * + * ospf + * OSPF pointer. + * + * nbr + * OSPF neighbour for which it is acting as HELPER. + * + * reason + * The reason for exiting from HELPER. + * + * Returns: + * Nothing. + */ +void ospf_gr_helper_exit(struct ospf_neighbor *nbr, + enum ospf_helper_exit_reason reason) +{ + struct ospf_interface *oi = nbr->oi; + struct ospf *ospf = oi->ospf; + + if (!OSPF_GR_IS_ACTIVE_HELPER(nbr)) + return; + + if (IS_DEBUG_OSPF_GR) + zlog_debug("%s, Exiting from HELPER support to %pI4, due to %s", + __func__, &nbr->src, ospf_exit_reason2str(reason)); + + /* Reset helper status*/ + nbr->gr_helper_info.gr_helper_status = OSPF_GR_NOT_HELPER; + nbr->gr_helper_info.helper_exit_reason = reason; + nbr->gr_helper_info.actual_grace_period = 0; + nbr->gr_helper_info.recvd_grace_period = 0; + nbr->gr_helper_info.gr_restart_reason = 0; + ospf->last_exit_reason = reason; + + if (ospf->active_restarter_cnt <= 0) { + zlog_err( + "OSPF GR-Helper: active_restarter_cnt should be greater than zero here."); + return; + } + /* Decrement active Restarter count */ + ospf->active_restarter_cnt--; + + /* If the exit not triggered due to grace timer + * expiry, stop the grace timer. + */ + if (reason != OSPF_GR_HELPER_GRACE_TIMEOUT) + EVENT_OFF(nbr->gr_helper_info.t_grace_timer); + + /* check exit triggered due to successful completion + * of graceful restart. + */ + if (reason != OSPF_GR_HELPER_COMPLETED) { + if (IS_DEBUG_OSPF_GR) + zlog_debug("%s, Unsuccessful GR exit", __func__); + } + + /*Recalculate the DR for the network segment */ + 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); + + /* Originate network lsa if it is an DR in the LAN */ + if (oi->state == ISM_DR) + ospf_network_lsa_update(oi); +} + +/* + * Process MaxAge Grace LSA. + * It is a indication for successful completion of GR. + * If router acting as HELPER, It exits from helper role. + * + * ospf + * OSPF pointer. + * + * lsa + * Grace LSA received from RESTARTER. + * + * nbr + * OSPF neighbour which requests the router to act as + * HELPER. + * + * Returns: + * Nothing. + */ +void ospf_process_maxage_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa, + struct ospf_neighbor *nbr) +{ + struct in_addr restartAddr = {0}; + uint8_t restartReason = 0; + uint32_t graceInterval = 0; + struct ospf_neighbor *restarter = NULL; + struct ospf_interface *oi = nbr->oi; + int ret; + + /* Extract the grace lsa packet fields */ + ret = ospf_extract_grace_lsa_fields(lsa, &graceInterval, &restartAddr, + &restartReason); + if (ret != OSPF_GR_SUCCESS) { + if (IS_DEBUG_OSPF_GR) + zlog_debug("%s, Wrong Grace LSA packet.", __func__); + return; + } + + if (IS_DEBUG_OSPF_GR) + zlog_debug("%s, GraceLSA received for neighbour %pI4", __func__, + &restartAddr); + + /* In case of broadcast links, if RESTARTER is DR_OTHER, + * grace LSA might be received from DR, so fetching the + * actual neighbour information using restarter address. + */ + if (oi->type != OSPF_IFTYPE_POINTOPOINT) { + restarter = ospf_nbr_lookup_by_addr(oi->nbrs, &restartAddr); + + if (!restarter) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, Restarter is not a neighbour for this router.", + __func__); + return; + } + } else { + restarter = nbr; + } + + ospf_gr_helper_exit(restarter, OSPF_GR_HELPER_COMPLETED); +} + +/* Configuration handlers */ +/* + * Disable/Enable HELPER support on router level. + * + * ospf + * OSPF pointer. + * + * status + * TRUE/FALSE + * + * Returns: + * Nothing. + */ +void ospf_gr_helper_support_set(struct ospf *ospf, bool support) +{ + struct ospf_interface *oi; + struct listnode *node; + struct advRtr lookup; + + if (ospf->is_helper_supported == support) + return; + + ospf->is_helper_supported = support; + + /* If helper support disabled, cease HELPER role for all + * supporting neighbors. + */ + if (support == OSPF_GR_FALSE) { + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + struct route_node *rn = NULL; + + if (ospf_interface_neighbor_count(oi) == 0) + continue; + + for (rn = route_top(oi->nbrs); rn; + rn = route_next(rn)) { + struct ospf_neighbor *nbr = NULL; + + if (!rn->info) + continue; + + nbr = rn->info; + + lookup.advRtrAddr.s_addr = + nbr->router_id.s_addr; + /* check if helper support enabled for the + * corresponding routerid.If enabled, don't + * exit from helper role. + */ + if (hash_lookup(ospf->enable_rtr_list, &lookup)) + continue; + + if (OSPF_GR_IS_ACTIVE_HELPER(nbr)) + ospf_gr_helper_exit( + nbr, OSPF_GR_HELPER_TOPO_CHG); + } + } + } +} + +/* + * Enable/Disable HELPER support on a specified advertagement + * router. + * + * ospf + * OSPF pointer. + * + * advRtr + * HELPER support for given Advertisement Router. + * + * support + * True - Enable Helper Support. + * False - Disable Helper Support. + * + * Returns: + * Nothing. + */ + +void ospf_gr_helper_support_set_per_routerid(struct ospf *ospf, + struct in_addr *advrtr, + bool support) +{ + struct advRtr temp; + struct advRtr *rtr; + struct ospf_interface *oi; + struct listnode *node; + + temp.advRtrAddr.s_addr = advrtr->s_addr; + + if (support == OSPF_GR_FALSE) { + /*Delete the routerid from the enable router hash table */ + rtr = hash_lookup(ospf->enable_rtr_list, &temp); + + if (rtr) { + hash_release(ospf->enable_rtr_list, rtr); + ospf_disable_rtr_hash_free(rtr); + } + + /* If helper support is enabled globally + * no action is required. + */ + if (ospf->is_helper_supported) + return; + + /* Cease the HELPER role fore neighbours from the + * specified advertisement router. + */ + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + struct route_node *rn = NULL; + + if (ospf_interface_neighbor_count(oi) == 0) + continue; + + for (rn = route_top(oi->nbrs); rn; + rn = route_next(rn)) { + struct ospf_neighbor *nbr = NULL; + + if (!rn->info) + continue; + + nbr = rn->info; + + if (nbr->router_id.s_addr != advrtr->s_addr) + continue; + + if (OSPF_GR_IS_ACTIVE_HELPER(nbr)) + ospf_gr_helper_exit( + nbr, OSPF_GR_HELPER_TOPO_CHG); + } + } + + } else { + /* Add the routerid to the enable router hash table */ + (void)hash_get(ospf->enable_rtr_list, &temp, + ospf_enable_rtr_hash_alloc); + } +} + +/* + * Api to enable/disable strict lsa check on the HELPER. + * + * ospf + * OSPF pointer. + * + * enabled + * True - disable the lsa check. + * False - enable the strict lsa check. + * + * Returns: + * Nothing. + */ +void ospf_gr_helper_lsa_check_set(struct ospf *ospf, bool enabled) +{ + if (ospf->strict_lsa_check == enabled) + return; + + ospf->strict_lsa_check = enabled; +} + +/* + * Api to set the supported grace interval in this router. + * + * ospf + * OSPF pointer. + * + * interval + * The supported grace interval.. + * + * Returns: + * Nothing. + */ +void ospf_gr_helper_supported_gracetime_set(struct ospf *ospf, + uint32_t interval) +{ + ospf->supported_grace_time = interval; +} + +/* + * Api to set the supported restart reason. + * + * ospf + * OSPF pointer. + * + * planned_only + * True: support only planned restart. + * False: support for planned/unplanned restarts. + * + * Returns: + * Nothing. + */ +void ospf_gr_helper_set_supported_planned_only_restart(struct ospf *ospf, + bool planned_only) +{ + ospf->only_planned_restart = planned_only; +} + +/* + * Api to display the grace LSA information. + * + * vty + * vty pointer. + * lsa + * Grace LSA. + * json + * json object + * + * Returns: + * Nothing. + */ +static void show_ospf_grace_lsa_info(struct vty *vty, struct json_object *json, + struct ospf_lsa *lsa) +{ + struct lsa_header *lsah = NULL; + struct tlv_header *tlvh = NULL; + struct grace_tlv_graceperiod *gracePeriod; + struct grace_tlv_restart_reason *grReason; + struct grace_tlv_restart_addr *restartAddr; + uint16_t length = 0; + int sum = 0; + + if (json) + return; + + lsah = (struct lsa_header *)lsa->data; + + if (lsa->size <= OSPF_LSA_HEADER_SIZE) { + 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; + + 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) { + 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)) { + 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); + + 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)) { + 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); + + 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)) { + 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); + + if (vty) + vty_out(vty, " Restarter address:%pI4\n", + &restartAddr->addr); + else + zlog_debug(" Restarter address:%pI4", + &restartAddr->addr); + break; + default: + 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_ia.c b/ospfd/ospf_ia.c new file mode 100644 index 0000000..59112b2 --- /dev/null +++ b/ospfd/ospf_ia.c @@ -0,0 +1,668 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF inter-area routing. + * Copyright (C) 1999, 2000 Alex Zinin, Toshiaki Takada + */ + + +#include + +#include "frrevent.h" +#include "memory.h" +#include "hash.h" +#include "linklist.h" +#include "prefix.h" +#include "table.h" +#include "log.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_ia.h" +#include "ospfd/ospf_dump.h" + +static struct ospf_route *ospf_find_abr_route(struct route_table *rtrs, + struct prefix_ipv4 *abr, + struct ospf_area *area) +{ + struct route_node *rn; + struct ospf_route * or ; + struct listnode *node; + + if ((rn = route_node_lookup(rtrs, (struct prefix *)abr)) == NULL) + return NULL; + + route_unlock_node(rn); + + for (ALL_LIST_ELEMENTS_RO((struct list *)rn->info, node, or)) + if (IPV4_ADDR_SAME(& or->u.std.area_id, &area->area_id) + && (or->u.std.flags & ROUTER_LSA_BORDER)) + return or ; + + return NULL; +} + +static void ospf_ia_network_route(struct ospf *ospf, struct route_table *rt, + struct prefix_ipv4 *p, + struct ospf_route *new_or, + struct ospf_route *abr_or) +{ + struct route_node *rn1; + struct ospf_route * or ; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: processing summary route to %pFX", __func__, p); + + /* Find a route to the same dest */ + if ((rn1 = route_node_lookup(rt, (struct prefix *)p))) { + int res; + + route_unlock_node(rn1); + + if ((or = rn1->info)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: Found a route to the same network", + __func__); + /* Check the existing route. */ + if ((res = ospf_route_cmp(ospf, new_or, or)) < 0) { + /* New route is better, so replace old one. */ + ospf_route_subst(rn1, new_or, abr_or); + } else if (res == 0) { + /* New and old route are equal, so next hops can + * be added. */ + route_lock_node(rn1); + ospf_route_copy_nexthops(or, abr_or->paths); + route_unlock_node(rn1); + + /* new route can be deleted, because existing + * route has been updated. */ + ospf_route_free(new_or); + } else { + /* New route is worse, so free it. */ + ospf_route_free(new_or); + return; + } + } /* if (or)*/ + } /*if (rn1)*/ + else { /* no route */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: add new route to %pFX", __func__, p); + ospf_route_add(rt, p, new_or, abr_or); + } +} + +static void ospf_ia_router_route(struct ospf *ospf, struct route_table *rtrs, + struct prefix_ipv4 *p, + struct ospf_route *new_or, + struct ospf_route *abr_or) +{ + struct ospf_route * or = NULL; + struct route_node *rn; + int ret; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: considering %pFX", __func__, p); + + /* Find a route to the same dest */ + rn = route_node_get(rtrs, (struct prefix *)p); + + if (rn->info == NULL) + /* This is a new route */ + rn->info = list_new(); + else { + struct ospf_area *or_area; + or_area = ospf_area_lookup_by_area_id(ospf, + new_or->u.std.area_id); + assert(or_area); + /* This is an additional route */ + route_unlock_node(rn); + or = ospf_find_asbr_route_through_area(rtrs, p, or_area); + } + + if (or) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: a route to the same ABR through the same area exists", + __func__); + /* New route is better */ + if ((ret = ospf_route_cmp(ospf, new_or, or)) < 0) { + listnode_delete(rn->info, or); + ospf_route_free(or); + /* proceed down */ + } + /* Routes are the same */ + else if (ret == 0) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: merging the new route", + __func__); + + ospf_route_copy_nexthops(or, abr_or->paths); + ospf_route_free(new_or); + return; + } + /* New route is worse */ + else { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: skipping the new route", + __func__); + ospf_route_free(new_or); + return; + } + } + + ospf_route_copy_nexthops(new_or, abr_or->paths); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: adding the new route", __func__); + + listnode_add(rn->info, new_or); +} + + +static int process_summary_lsa(struct ospf_area *area, struct route_table *rt, + struct route_table *rtrs, struct ospf_lsa *lsa) +{ + struct ospf *ospf = area->ospf; + struct ospf_area_range *range; + struct ospf_route *abr_or, *new_or; + struct summary_lsa *sl; + struct prefix_ipv4 p, abr; + uint32_t metric; + + if (lsa == NULL) + return 0; + + sl = (struct summary_lsa *)lsa->data; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: LS ID: %pI4", __func__, &sl->header.id); + + metric = GET_METRIC(sl->metric); + + if (metric == OSPF_LS_INFINITY) + return 0; + + if (IS_LSA_MAXAGE(lsa)) + return 0; + + if (ospf_lsa_is_self_originated(area->ospf, lsa)) + return 0; + + p.family = AF_INET; + p.prefix = sl->header.id; + + if (sl->header.type == OSPF_SUMMARY_LSA) + p.prefixlen = ip_masklen(sl->mask); + else + p.prefixlen = IPV4_MAX_BITLEN; + + apply_mask_ipv4(&p); + + if (sl->header.type == OSPF_SUMMARY_LSA + && (range = ospf_area_range_match_any(ospf, &p)) + && ospf_area_range_active(range)) + return 0; + + /* XXX: This check seems dubious to me. If an ABR has already decided + * to consider summaries received in this area, then why would one wish + * to exclude default? + */ + if (IS_OSPF_ABR(ospf) && ospf->abr_type != OSPF_ABR_STAND + && area->external_routing != OSPF_AREA_DEFAULT + && p.prefix.s_addr == OSPF_DEFAULT_DESTINATION && p.prefixlen == 0) + return 0; /* Ignore summary default from a stub area */ + + abr.family = AF_INET; + abr.prefix = sl->header.adv_router; + abr.prefixlen = IPV4_MAX_BITLEN; + apply_mask_ipv4(&abr); + + abr_or = ospf_find_abr_route(rtrs, &abr, area); + + if (abr_or == NULL) + return 0; + + new_or = ospf_route_new(); + new_or->type = OSPF_DESTINATION_NETWORK; + new_or->id = sl->header.id; + new_or->mask = sl->mask; + new_or->u.std.options = sl->header.options; + new_or->u.std.origin = (struct lsa_header *)sl; + new_or->cost = abr_or->cost + metric; + new_or->u.std.area_id = area->area_id; + new_or->u.std.external_routing = area->external_routing; + new_or->path_type = OSPF_PATH_INTER_AREA; + + if (sl->header.type == OSPF_SUMMARY_LSA) + ospf_ia_network_route(ospf, rt, &p, new_or, abr_or); + else { + new_or->type = OSPF_DESTINATION_ROUTER; + new_or->u.std.flags = ROUTER_LSA_EXTERNAL; + ospf_ia_router_route(ospf, rtrs, &p, new_or, abr_or); + } + + return 0; +} + +static void ospf_examine_summaries(struct ospf_area *area, + struct route_table *lsdb_rt, + struct route_table *rt, + struct route_table *rtrs) +{ + struct ospf_lsa *lsa; + struct route_node *rn; + + LSDB_LOOP (lsdb_rt, rn, lsa) + process_summary_lsa(area, rt, rtrs, lsa); +} + +int ospf_area_is_transit(struct ospf_area *area) +{ + return (area->transit == OSPF_TRANSIT_TRUE) + || ospf_full_virtual_nbrs( + area); /* Cisco forgets to set the V-bit :( */ +} + +static void ospf_update_network_route(struct ospf *ospf, struct route_table *rt, + struct route_table *rtrs, + struct summary_lsa *lsa, + struct prefix_ipv4 *p, + struct ospf_area *area) +{ + struct route_node *rn; + struct ospf_route * or, *abr_or, *new_or; + struct prefix_ipv4 abr; + uint32_t cost; + + abr.family = AF_INET; + abr.prefix = lsa->header.adv_router; + abr.prefixlen = IPV4_MAX_BITLEN; + apply_mask_ipv4(&abr); + + abr_or = ospf_find_abr_route(rtrs, &abr, area); + + if (abr_or == NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: can't find a route to the ABR", + __func__); + return; + } + + cost = abr_or->cost + GET_METRIC(lsa->metric); + + rn = route_node_lookup(rt, (struct prefix *)p); + + if (!rn) { + if (ospf->abr_type != OSPF_ABR_SHORTCUT) + return; /* Standard ABR can update only already + installed + backbone paths */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Allowing Shortcut ABR to add new route", + __func__); + new_or = ospf_route_new(); + new_or->type = OSPF_DESTINATION_NETWORK; + new_or->id = lsa->header.id; + new_or->mask = lsa->mask; + new_or->u.std.options = lsa->header.options; + new_or->u.std.origin = (struct lsa_header *)lsa; + new_or->cost = cost; + new_or->u.std.area_id = area->area_id; + new_or->u.std.external_routing = area->external_routing; + new_or->path_type = OSPF_PATH_INTER_AREA; + ospf_route_add(rt, p, new_or, abr_or); + + return; + } else { + route_unlock_node(rn); + if (rn->info == NULL) + return; + } + + or = rn->info; + + if (or->path_type != OSPF_PATH_INTRA_AREA && + or->path_type != OSPF_PATH_INTER_AREA) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: ERR: path type is wrong", __func__); + return; + } + + if (ospf->abr_type == OSPF_ABR_SHORTCUT) { + if ( + or->path_type == OSPF_PATH_INTRA_AREA + && !OSPF_IS_AREA_ID_BACKBONE( + or->u.std.area_id)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: Shortcut: this intra-area path is not backbone", + __func__); + return; + } + } else /* Not Shortcut ABR */ + { + if (!OSPF_IS_AREA_ID_BACKBONE(or->u.std.area_id)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: route is not BB-associated", + __func__); + return; /* We can update only BB routes */ + } + } + + if (or->cost < cost) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: new route is worse", __func__); + return; + } + + if (or->cost == cost) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: new route is same distance, adding nexthops", + __func__); + ospf_route_copy_nexthops(or, abr_or->paths); + } + + if (or->cost > cost) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: new route is better, overriding nexthops", + __func__); + ospf_route_subst_nexthops(or, abr_or->paths); + or->cost = cost; + + if ((ospf->abr_type == OSPF_ABR_SHORTCUT) + && !OSPF_IS_AREA_ID_BACKBONE(or->u.std.area_id)) { + or->path_type = OSPF_PATH_INTER_AREA; + or->u.std.area_id = area->area_id; + or->u.std.external_routing = area->external_routing; + /* Note that we can do this only in Shortcut ABR mode, + because standard ABR must leave the route type and + area + unchanged + */ + } + } +} + +static void ospf_update_router_route(struct ospf *ospf, + struct route_table *rtrs, + struct summary_lsa *lsa, + struct prefix_ipv4 *p, + struct ospf_area *area) +{ + struct ospf_route * or, *abr_or, *new_or; + struct prefix_ipv4 abr; + uint32_t cost; + + abr.family = AF_INET; + abr.prefix = lsa->header.adv_router; + abr.prefixlen = IPV4_MAX_BITLEN; + apply_mask_ipv4(&abr); + + abr_or = ospf_find_abr_route(rtrs, &abr, area); + + if (abr_or == NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: can't find a route to the ABR", + __func__); + return; + } + + cost = abr_or->cost + GET_METRIC(lsa->metric); + + /* First try to find a backbone path, + because standard ABR can update only BB-associated paths */ + + if ((ospf->backbone == NULL) && (ospf->abr_type != OSPF_ABR_SHORTCUT)) + return; /* no BB area, not Shortcut ABR, exiting */ + + /* find the backbone route, if possible */ + if ((ospf->backbone == NULL) + || !(or = ospf_find_asbr_route_through_area(rtrs, p, + ospf->backbone))) { + if (ospf->abr_type != OSPF_ABR_SHORTCUT) + + /* route to ASBR through the BB not found + the router is not Shortcut ABR, exiting */ + + return; + else + /* We're a Shortcut ABR*/ + { + /* Let it either add a new router or update the route + through the same (non-BB) area. */ + + new_or = ospf_route_new(); + new_or->type = OSPF_DESTINATION_ROUTER; + new_or->id = lsa->header.id; + new_or->mask = lsa->mask; + new_or->u.std.options = lsa->header.options; + new_or->u.std.origin = (struct lsa_header *)lsa; + new_or->cost = cost; + new_or->u.std.area_id = area->area_id; + new_or->u.std.external_routing = area->external_routing; + new_or->path_type = OSPF_PATH_INTER_AREA; + new_or->u.std.flags = ROUTER_LSA_EXTERNAL; + ospf_ia_router_route(ospf, rtrs, p, new_or, abr_or); + + return; + } + } + + /* At this point the "or" is always bb-associated */ + + if (!(or->u.std.flags & ROUTER_LSA_EXTERNAL)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: the remote router is not an ASBR", + __func__); + return; + } + + if (or->path_type != OSPF_PATH_INTRA_AREA && + or->path_type != OSPF_PATH_INTER_AREA) + return; + + if (or->cost < cost) + return; + + else if (or->cost == cost) + ospf_route_copy_nexthops(or, abr_or->paths); + + else if (or->cost > cost) { + ospf_route_subst_nexthops(or, abr_or->paths); + or->cost = cost; + + /* Even if the ABR runs in Shortcut mode, we can't change + the path type and area, because the "or" is always + bb-associated + at this point and even Shortcut ABR can't change these + attributes */ + } +} + +static int process_transit_summary_lsa(struct ospf_area *area, + struct route_table *rt, + struct route_table *rtrs, + struct ospf_lsa *lsa) +{ + struct ospf *ospf = area->ospf; + struct summary_lsa *sl; + struct prefix_ipv4 p; + uint32_t metric; + + if (lsa == NULL) + return 0; + + sl = (struct summary_lsa *)lsa->data; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: LS ID: %pI4", __func__, &lsa->data->id); + metric = GET_METRIC(sl->metric); + + if (metric == OSPF_LS_INFINITY) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: metric is infinity, skip", __func__); + return 0; + } + + if (IS_LSA_MAXAGE(lsa)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: This LSA is too old", __func__); + return 0; + } + + if (ospf_lsa_is_self_originated(area->ospf, lsa)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: This LSA is mine, skip", __func__); + return 0; + } + + p.family = AF_INET; + p.prefix = sl->header.id; + + if (sl->header.type == OSPF_SUMMARY_LSA) + p.prefixlen = ip_masklen(sl->mask); + else + p.prefixlen = IPV4_MAX_BITLEN; + + apply_mask_ipv4(&p); + + if (sl->header.type == OSPF_SUMMARY_LSA) + ospf_update_network_route(ospf, rt, rtrs, sl, &p, area); + else + ospf_update_router_route(ospf, rtrs, sl, &p, area); + + return 0; +} + +static void ospf_examine_transit_summaries(struct ospf_area *area, + struct route_table *lsdb_rt, + struct route_table *rt, + struct route_table *rtrs) +{ + struct ospf_lsa *lsa; + struct route_node *rn; + + LSDB_LOOP (lsdb_rt, rn, lsa) + process_transit_summary_lsa(area, rt, rtrs, lsa); +} + +void ospf_ia_routing(struct ospf *ospf, struct route_table *rt, + struct route_table *rtrs) +{ + struct listnode *node; + struct ospf_area *area; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s:start", __func__); + + if (IS_OSPF_ABR(ospf)) { + switch (ospf->abr_type) { + case OSPF_ABR_STAND: + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s:Standard ABR", __func__); + + if ((area = ospf->backbone)) { + if (IS_DEBUG_OSPF_EVENT) { + zlog_debug( + "%s:backbone area found, examining summaries", + __func__); + } + + OSPF_EXAMINE_SUMMARIES_ALL(area, rt, rtrs); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, + area)) + if (area != ospf->backbone) + if (ospf_area_is_transit(area)) + OSPF_EXAMINE_TRANSIT_SUMMARIES_ALL( + area, rt, rtrs); + } else if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s:backbone area NOT found", + __func__); + break; + case OSPF_ABR_IBM: + case OSPF_ABR_CISCO: + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s:Alternative Cisco/IBM ABR", + __func__); + area = ospf->backbone; /* Find the BB */ + + /* If we have an active BB connection */ + if (area && ospf_act_bb_connection(ospf)) { + if (IS_DEBUG_OSPF_EVENT) { + zlog_debug( + "%s: backbone area found, examining BB summaries", + __func__); + } + + OSPF_EXAMINE_SUMMARIES_ALL(area, rt, rtrs); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, + area)) + if (area != ospf->backbone) + if (ospf_area_is_transit(area)) + OSPF_EXAMINE_TRANSIT_SUMMARIES_ALL( + area, rt, rtrs); + } else { /* No active BB connection--consider all areas + */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: Active BB connection not found", + __func__); + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, + area)) + OSPF_EXAMINE_SUMMARIES_ALL(area, rt, + rtrs); + } + break; + case OSPF_ABR_SHORTCUT: + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s:Alternative Shortcut", __func__); + area = ospf->backbone; /* Find the BB */ + + /* If we have an active BB connection */ + if (area && ospf_act_bb_connection(ospf)) { + if (IS_DEBUG_OSPF_EVENT) { + zlog_debug( + "%s: backbone area found, examining BB summaries", + __func__); + } + OSPF_EXAMINE_SUMMARIES_ALL(area, rt, rtrs); + } + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) + if (area != ospf->backbone) + if (ospf_area_is_transit(area) + || ((area->shortcut_configured + != OSPF_SHORTCUT_DISABLE) + && ((ospf->backbone == NULL) + || ((area->shortcut_configured + == OSPF_SHORTCUT_ENABLE) + && area->shortcut_capability)))) + OSPF_EXAMINE_TRANSIT_SUMMARIES_ALL( + area, rt, rtrs); + break; + default: + break; + } + } else { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s:not ABR, considering all areas", + __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) + OSPF_EXAMINE_SUMMARIES_ALL(area, rt, rtrs); + } +} diff --git a/ospfd/ospf_ia.h b/ospfd/ospf_ia.h new file mode 100644 index 0000000..1005f2b --- /dev/null +++ b/ospfd/ospf_ia.h @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF inter-area routing. + * Copyright (C) 1999, 2000 Alex Zinin, Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPF_IA_H +#define _ZEBRA_OSPF_IA_H + +/* Macros. */ +#define OSPF_EXAMINE_SUMMARIES_ALL(A, N, R) \ + { \ + ospf_examine_summaries((A), SUMMARY_LSDB((A)), (N), (R)); \ + ospf_examine_summaries((A), ASBR_SUMMARY_LSDB((A)), (N), (R)); \ + } + +#define OSPF_EXAMINE_TRANSIT_SUMMARIES_ALL(A, N, R) \ + { \ + ospf_examine_transit_summaries((A), SUMMARY_LSDB((A)), (N), \ + (R)); \ + ospf_examine_transit_summaries((A), ASBR_SUMMARY_LSDB((A)), \ + (N), (R)); \ + } + +extern void ospf_ia_routing(struct ospf *, struct route_table *, + struct route_table *); +extern int ospf_area_is_transit(struct ospf_area *); + +#endif /* _ZEBRA_OSPF_IA_H */ diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c new file mode 100644 index 0000000..7601419 --- /dev/null +++ b/ospfd/ospf_interface.c @@ -0,0 +1,1571 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF Interface functions. + * Copyright (C) 1999, 2000 Toshiaki Takada + */ + +#include + +#include "frrevent.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "memory.h" +#include "command.h" +#include "stream.h" +#include "log.h" +#include "network.h" +#include "zclient.h" +#include "bfd.h" +#include "ldp_sync.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_bfd.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_network.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_ldp_sync.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_te.h" + +DEFINE_QOBJ_TYPE(ospf_interface); +DEFINE_HOOK(ospf_vl_add, (struct ospf_vl_data * vd), (vd)); +DEFINE_HOOK(ospf_vl_delete, (struct ospf_vl_data * vd), (vd)); +DEFINE_HOOK(ospf_if_update, (struct interface * ifp), (ifp)); +DEFINE_HOOK(ospf_if_delete, (struct interface * ifp), (ifp)); + +int ospf_interface_neighbor_count(struct ospf_interface *oi) +{ + int count = 0; + struct route_node *rn; + struct ospf_neighbor *nbr = NULL; + + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + nbr = rn->info; + if (!nbr) + continue; + + /* Do not show myself. */ + if (nbr == oi->nbr_self) + continue; + /* Down state is not shown. */ + if (nbr->state == NSM_Down) + continue; + count++; + } + + return count; +} + +int ospf_if_get_output_cost(struct ospf_interface *oi) +{ + /* If all else fails, use default OSPF cost */ + uint32_t cost; + uint32_t bw, refbw; + + /* if LDP-IGP Sync is running on interface set cost so interface + * is used only as last resort + */ + if (ldp_sync_if_is_enabled(IF_DEF_PARAMS(oi->ifp)->ldp_sync_info)) + return (LDP_OSPF_LSINFINITY); + + /* ifp speed and bw can be 0 in some platforms, use ospf default bw + if bw is configured under interface it would be used. + */ + if (!oi->ifp->bandwidth && oi->ifp->speed) + bw = oi->ifp->speed; + else + bw = oi->ifp->bandwidth ? oi->ifp->bandwidth + : OSPF_DEFAULT_BANDWIDTH; + refbw = oi->ospf->ref_bandwidth; + + /* A specified ip ospf cost overrides a calculated one. */ + if (OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(oi->ifp), output_cost_cmd) + || OSPF_IF_PARAM_CONFIGURED(oi->params, output_cost_cmd)) + cost = OSPF_IF_PARAM(oi, output_cost_cmd); + /* See if a cost can be calculated from the zebra processes + interface bandwidth field. */ + else { + cost = (uint32_t)((double)refbw / (double)bw + (double)0.5); + if (cost < 1) + cost = 1; + else if (cost > 65535) + cost = 65535; + + if (if_is_loopback(oi->ifp)) + cost = 0; + } + + return cost; +} + +void ospf_if_recalculate_output_cost(struct interface *ifp) +{ + uint32_t newcost; + struct route_node *rn; + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi; + + if ((oi = rn->info) == NULL) + continue; + + newcost = ospf_if_get_output_cost(oi); + + /* Is actual output cost changed? */ + if (oi->output_cost != newcost) { + oi->output_cost = newcost; + ospf_router_lsa_update_area(oi->area); + } + } +} + +/* Simulate down/up on the interface. This is needed, for example, when + the MTU changes. */ +void ospf_if_reset(struct interface *ifp) +{ + struct route_node *rn; + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi; + + if ((oi = rn->info) == NULL) + continue; + + ospf_if_down(oi); + ospf_if_up(oi); + } +} + +void ospf_if_reset_variables(struct ospf_interface *oi) +{ + /* Set default values. */ + /* don't clear this flag. oi->flag = OSPF_IF_DISABLE; */ + + if (oi->vl_data) + oi->type = OSPF_IFTYPE_VIRTUALLINK; + else + /* preserve network-type */ + if (oi->type != OSPF_IFTYPE_NBMA) + oi->type = OSPF_IFTYPE_BROADCAST; + + oi->state = ISM_Down; + + oi->crypt_seqnum = 0; + + /* This must be short, (less than RxmtInterval) + - RFC 2328 Section 13.5 para 3. Set to 1 second to avoid Acks being + held back for too long - MAG */ + oi->v_ls_ack = 1; +} + +/* lookup oi for specified prefix/ifp */ +struct ospf_interface *ospf_if_table_lookup(struct interface *ifp, + struct prefix *prefix) +{ + struct prefix p; + struct route_node *rn; + struct ospf_interface *rninfo = NULL; + + p = *prefix; + p.prefixlen = IPV4_MAX_BITLEN; + + /* route_node_get implicitely locks */ + if ((rn = route_node_lookup(IF_OIFS(ifp), &p))) { + rninfo = (struct ospf_interface *)rn->info; + route_unlock_node(rn); + } + + return rninfo; +} + +static void ospf_add_to_if(struct interface *ifp, struct ospf_interface *oi) +{ + struct route_node *rn; + struct prefix p; + + p = *oi->address; + p.prefixlen = IPV4_MAX_BITLEN; + apply_mask(&p); + + rn = route_node_get(IF_OIFS(ifp), &p); + /* rn->info should either be NULL or equal to this oi + * as route_node_get may return an existing node + */ + assert(!rn->info || rn->info == oi); + rn->info = oi; +} + +static void ospf_delete_from_if(struct interface *ifp, + struct ospf_interface *oi) +{ + struct route_node *rn; + struct prefix p; + + p = *oi->address; + p.prefixlen = IPV4_MAX_BITLEN; + + rn = route_node_lookup(IF_OIFS(oi->ifp), &p); + assert(rn); + assert(rn->info); + rn->info = NULL; + route_unlock_node(rn); + route_unlock_node(rn); +} + +struct ospf_interface *ospf_if_new(struct ospf *ospf, struct interface *ifp, + struct prefix *p) +{ + struct ospf_interface *oi; + + oi = ospf_if_table_lookup(ifp, p); + if (oi) + return oi; + + oi = XCALLOC(MTYPE_OSPF_IF, sizeof(struct ospf_interface)); + + oi->obuf = ospf_fifo_new(); + + /* Set zebra interface pointer. */ + oi->ifp = ifp; + oi->address = p; + + ospf_add_to_if(ifp, oi); + listnode_add(ospf->oiflist, oi); + + /* Initialize neighbor list. */ + oi->nbrs = route_table_init(); + + /* Initialize static neighbor list. */ + oi->nbr_nbma = list_new(); + + /* Initialize Link State Acknowledgment list. */ + oi->ls_ack = list_new(); + oi->ls_ack_direct.ls_ack = list_new(); + + /* Set default values. */ + ospf_if_reset_variables(oi); + + /* Set pseudo neighbor to Null */ + oi->nbr_self = NULL; + + oi->ls_upd_queue = route_table_init(); + oi->t_ls_upd_event = NULL; + oi->t_ls_ack_direct = NULL; + + oi->crypt_seqnum = frr_sequence32_next(); + + ospf_opaque_type9_lsa_init(oi); + + oi->ospf = ospf; + + QOBJ_REG(oi, ospf_interface); + + /* If first oi, check per-intf write socket */ + if (ospf->oi_running && ospf->intf_socket_enabled) + ospf_ifp_sock_init(ifp); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: ospf interface %s vrf %s id %u created", + __func__, ifp->name, ospf_get_name(ospf), + ospf->vrf_id); + + return oi; +} + +/* Restore an interface to its pre UP state + Used from ism_interface_down only */ +void ospf_if_cleanup(struct ospf_interface *oi) +{ + struct route_node *rn; + struct listnode *node, *nnode; + struct ospf_neighbor *nbr; + struct ospf_nbr_nbma *nbr_nbma; + struct ospf_lsa *lsa; + + /* oi->nbrs and oi->nbr_nbma should be deleted on InterfaceDown event */ + /* delete all static neighbors attached to this interface */ + for (ALL_LIST_ELEMENTS(oi->nbr_nbma, node, nnode, nbr_nbma)) { + EVENT_OFF(nbr_nbma->t_poll); + + if (nbr_nbma->nbr) { + nbr_nbma->nbr->nbr_nbma = NULL; + nbr_nbma->nbr = NULL; + } + + nbr_nbma->oi = NULL; + + listnode_delete(oi->nbr_nbma, nbr_nbma); + } + + /* send Neighbor event KillNbr to all associated neighbors. */ + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + if ((nbr = rn->info) != NULL) + if (nbr != oi->nbr_self) + OSPF_NSM_EVENT_EXECUTE(nbr, NSM_KillNbr); + } + + /* Cleanup Link State Acknowlegdment list. */ + for (ALL_LIST_ELEMENTS(oi->ls_ack, node, nnode, lsa)) + ospf_lsa_unlock(&lsa); /* oi->ls_ack */ + list_delete_all_node(oi->ls_ack); + + oi->crypt_seqnum = 0; + + /* Empty link state update queue */ + ospf_ls_upd_queue_empty(oi); + + /* Reset pseudo neighbor. */ + ospf_nbr_self_reset(oi, oi->ospf->router_id); +} + +void ospf_if_free(struct ospf_interface *oi) +{ + struct interface *ifp = oi->ifp; + + ospf_if_down(oi); + + ospf_fifo_free(oi->obuf); + + assert(oi->state == ISM_Down); + + ospf_opaque_type9_lsa_term(oi); + + QOBJ_UNREG(oi); + + /* Free Pseudo Neighbour */ + ospf_nbr_delete(oi->nbr_self); + + route_table_finish(oi->nbrs); + route_table_finish(oi->ls_upd_queue); + + /* Free any lists that should be freed */ + list_delete(&oi->nbr_nbma); + + list_delete(&oi->ls_ack); + list_delete(&oi->ls_ack_direct.ls_ack); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: ospf interface %s vrf %s id %u deleted", + __func__, oi->ifp->name, oi->ifp->vrf->name, + oi->ifp->vrf->vrf_id); + + ospf_delete_from_if(oi->ifp, oi); + + listnode_delete(oi->ospf->oiflist, oi); + listnode_delete(oi->area->oiflist, oi); + + event_cancel_event(master, oi); + + /* If last oi, close per-interface socket */ + if (ospf_oi_count(ifp) == 0) + ospf_ifp_sock_close(ifp); + + memset(oi, 0, sizeof(*oi)); + XFREE(MTYPE_OSPF_IF, oi); +} + +int ospf_if_is_up(struct ospf_interface *oi) +{ + return if_is_up(oi->ifp); +} + +struct ospf_interface *ospf_if_exists(struct ospf_interface *oic) +{ + struct listnode *node; + struct ospf *ospf; + struct ospf_interface *oi; + + if (!oic) + return NULL; + + ospf = oic->ospf; + if (ospf == NULL) + return NULL; + + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) + if (oi == oic) + return oi; + + return NULL; +} + +/* Lookup OSPF interface by router LSA posistion */ +struct ospf_interface *ospf_if_lookup_by_lsa_pos(struct ospf_area *area, + int lsa_pos) +{ + struct listnode *node; + struct ospf_interface *oi; + + for (ALL_LIST_ELEMENTS_RO(area->oiflist, node, oi)) { + if (lsa_pos >= oi->lsa_pos_beg && lsa_pos < oi->lsa_pos_end) + return oi; + } + return NULL; +} + +struct ospf_interface *ospf_if_lookup_by_local_addr(struct ospf *ospf, + struct interface *ifp, + struct in_addr address) +{ + struct listnode *node; + struct ospf_interface *oi; + + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) { + if (ifp && oi->ifp != ifp) + continue; + + if (IPV4_ADDR_SAME(&address, &oi->address->u.prefix4)) + return oi; + } + + return NULL; +} + +struct ospf_interface *ospf_if_lookup_by_prefix(struct ospf *ospf, + struct prefix_ipv4 *p) +{ + struct listnode *node; + struct ospf_interface *oi; + + /* Check each Interface. */ + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) { + struct prefix ptmp; + + prefix_copy(&ptmp, CONNECTED_PREFIX(oi->connected)); + apply_mask(&ptmp); + if (prefix_same(&ptmp, (struct prefix *)p)) + return oi; + } + } + return NULL; +} + +/* determine receiving interface by ifp and source address */ +struct ospf_interface *ospf_if_lookup_recv_if(struct ospf *ospf, + struct in_addr src, + struct interface *ifp) +{ + struct route_node *rn; + struct prefix_ipv4 addr; + struct ospf_interface *oi, *match, *unnumbered_match; + + addr.family = AF_INET; + addr.prefix = src; + addr.prefixlen = IPV4_MAX_BITLEN; + + match = unnumbered_match = NULL; + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + oi = rn->info; + + if (!oi) /* oi can be NULL for PtP aliases */ + continue; + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + continue; + + if (if_is_loopback(oi->ifp)) + continue; + + if (CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) + unnumbered_match = oi; + else if (prefix_match(CONNECTED_PREFIX(oi->connected), + (struct prefix *)&addr)) { + if ((match == NULL) || (match->address->prefixlen + < oi->address->prefixlen)) + match = oi; + } + } + + if (match) + return match; + + return unnumbered_match; +} + +void ospf_interface_fifo_flush(struct ospf_interface *oi) +{ + struct ospf *ospf = oi->ospf; + + ospf_fifo_flush(oi->obuf); + + if (oi->on_write_q) { + listnode_delete(ospf->oi_write_q, oi); + if (list_isempty(ospf->oi_write_q)) + EVENT_OFF(ospf->t_write); + oi->on_write_q = 0; + } +} + +static void ospf_if_reset_stats(struct ospf_interface *oi) +{ + oi->hello_in = oi->hello_out = 0; + oi->db_desc_in = oi->db_desc_out = 0; + oi->ls_req_in = oi->ls_req_out = 0; + oi->ls_upd_in = oi->ls_upd_out = 0; + oi->ls_ack_in = oi->ls_ack_out = 0; +} + +void ospf_if_stream_unset(struct ospf_interface *oi) +{ + /* flush the interface packet queue */ + ospf_interface_fifo_flush(oi); + /*reset protocol stats */ + ospf_if_reset_stats(oi); +} + + +static struct ospf_if_params *ospf_new_if_params(void) +{ + struct ospf_if_params *oip; + + oip = XCALLOC(MTYPE_OSPF_IF_PARAMS, sizeof(struct ospf_if_params)); + + UNSET_IF_PARAM(oip, output_cost_cmd); + UNSET_IF_PARAM(oip, transmit_delay); + UNSET_IF_PARAM(oip, retransmit_interval); + UNSET_IF_PARAM(oip, passive_interface); + UNSET_IF_PARAM(oip, v_hello); + UNSET_IF_PARAM(oip, fast_hello); + UNSET_IF_PARAM(oip, v_gr_hello_delay); + UNSET_IF_PARAM(oip, v_wait); + UNSET_IF_PARAM(oip, priority); + UNSET_IF_PARAM(oip, type); + UNSET_IF_PARAM(oip, auth_simple); + UNSET_IF_PARAM(oip, auth_crypt); + UNSET_IF_PARAM(oip, auth_type); + UNSET_IF_PARAM(oip, if_area); + UNSET_IF_PARAM(oip, opaque_capable); + UNSET_IF_PARAM(oip, keychain_name); + + oip->auth_crypt = list_new(); + + oip->network_lsa_seqnum = htonl(OSPF_INITIAL_SEQUENCE_NUMBER); + oip->is_v_wait_set = false; + + oip->ptp_dmvpn = 0; + oip->p2mp_delay_reflood = OSPF_P2MP_DELAY_REFLOOD_DEFAULT; + oip->opaque_capable = OSPF_OPAQUE_CAPABLE_DEFAULT; + + return oip; +} + +static void ospf_del_if_params(struct interface *ifp, + struct ospf_if_params *oip) +{ + list_delete(&oip->auth_crypt); + XFREE(MTYPE_OSPF_IF_PARAMS, oip->keychain_name); + ospf_interface_disable_bfd(ifp, oip); + ldp_sync_info_free(&(oip->ldp_sync_info)); + XFREE(MTYPE_OSPF_IF_PARAMS, oip); +} + +void ospf_free_if_params(struct interface *ifp, struct in_addr addr) +{ + struct ospf_if_params *oip; + struct prefix_ipv4 p; + struct route_node *rn; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.prefix = addr; + rn = route_node_lookup(IF_OIFS_PARAMS(ifp), (struct prefix *)&p); + if (!rn || !rn->info) + return; + + oip = rn->info; + route_unlock_node(rn); + + if (!OSPF_IF_PARAM_CONFIGURED(oip, output_cost_cmd) && + !OSPF_IF_PARAM_CONFIGURED(oip, transmit_delay) && + !OSPF_IF_PARAM_CONFIGURED(oip, retransmit_interval) && + !OSPF_IF_PARAM_CONFIGURED(oip, passive_interface) && + !OSPF_IF_PARAM_CONFIGURED(oip, v_hello) && + !OSPF_IF_PARAM_CONFIGURED(oip, fast_hello) && + !OSPF_IF_PARAM_CONFIGURED(oip, v_wait) && + !OSPF_IF_PARAM_CONFIGURED(oip, priority) && + !OSPF_IF_PARAM_CONFIGURED(oip, type) && + !OSPF_IF_PARAM_CONFIGURED(oip, auth_simple) && + !OSPF_IF_PARAM_CONFIGURED(oip, auth_type) && + !OSPF_IF_PARAM_CONFIGURED(oip, if_area) && + !OSPF_IF_PARAM_CONFIGURED(oip, opaque_capable) && + !OSPF_IF_PARAM_CONFIGURED(oip, prefix_suppression) && + !OSPF_IF_PARAM_CONFIGURED(oip, keychain_name) && + listcount(oip->auth_crypt) == 0) { + ospf_del_if_params(ifp, oip); + rn->info = NULL; + route_unlock_node(rn); + } +} + +struct ospf_if_params *ospf_lookup_if_params(struct interface *ifp, + struct in_addr addr) +{ + struct prefix_ipv4 p; + struct route_node *rn; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.prefix = addr; + + rn = route_node_lookup(IF_OIFS_PARAMS(ifp), (struct prefix *)&p); + + if (rn) { + route_unlock_node(rn); + return rn->info; + } + + return NULL; +} + +struct ospf_if_params *ospf_get_if_params(struct interface *ifp, + struct in_addr addr) +{ + struct prefix_ipv4 p; + struct route_node *rn; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.prefix = addr; + apply_mask_ipv4(&p); + + rn = route_node_get(IF_OIFS_PARAMS(ifp), (struct prefix *)&p); + + if (rn->info == NULL) + rn->info = ospf_new_if_params(); + else + route_unlock_node(rn); + + return rn->info; +} + +void ospf_if_update_params(struct interface *ifp, struct in_addr addr) +{ + struct route_node *rn; + struct ospf_interface *oi; + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + if ((oi = rn->info) == NULL) + continue; + + if (IPV4_ADDR_SAME(&oi->address->u.prefix4, &addr)) + oi->params = ospf_lookup_if_params( + ifp, oi->address->u.prefix4); + } +} + +int ospf_if_new_hook(struct interface *ifp) +{ + int rc = 0; + + ifp->info = XCALLOC(MTYPE_OSPF_IF_INFO, sizeof(struct ospf_if_info)); + + IF_OSPF_IF_INFO(ifp)->oii_fd = -1; + + IF_OIFS(ifp) = route_table_init(); + IF_OIFS_PARAMS(ifp) = route_table_init(); + + IF_DEF_PARAMS(ifp) = ospf_new_if_params(); + + SET_IF_PARAM(IF_DEF_PARAMS(ifp), transmit_delay); + IF_DEF_PARAMS(ifp)->transmit_delay = OSPF_TRANSMIT_DELAY_DEFAULT; + + SET_IF_PARAM(IF_DEF_PARAMS(ifp), retransmit_interval); + IF_DEF_PARAMS(ifp)->retransmit_interval = + OSPF_RETRANSMIT_INTERVAL_DEFAULT; + + SET_IF_PARAM(IF_DEF_PARAMS(ifp), priority); + IF_DEF_PARAMS(ifp)->priority = OSPF_ROUTER_PRIORITY_DEFAULT; + + IF_DEF_PARAMS(ifp)->mtu_ignore = OSPF_MTU_IGNORE_DEFAULT; + + SET_IF_PARAM(IF_DEF_PARAMS(ifp), v_hello); + IF_DEF_PARAMS(ifp)->v_hello = OSPF_HELLO_INTERVAL_DEFAULT; + + SET_IF_PARAM(IF_DEF_PARAMS(ifp), fast_hello); + IF_DEF_PARAMS(ifp)->fast_hello = OSPF_FAST_HELLO_DEFAULT; + + SET_IF_PARAM(IF_DEF_PARAMS(ifp), v_gr_hello_delay); + IF_DEF_PARAMS(ifp)->v_gr_hello_delay = OSPF_HELLO_DELAY_DEFAULT; + + SET_IF_PARAM(IF_DEF_PARAMS(ifp), v_wait); + IF_DEF_PARAMS(ifp)->v_wait = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; + + SET_IF_PARAM(IF_DEF_PARAMS(ifp), auth_simple); + memset(IF_DEF_PARAMS(ifp)->auth_simple, 0, OSPF_AUTH_SIMPLE_SIZE); + + SET_IF_PARAM(IF_DEF_PARAMS(ifp), auth_type); + IF_DEF_PARAMS(ifp)->auth_type = OSPF_AUTH_NOTSET; + + SET_IF_PARAM(IF_DEF_PARAMS(ifp), opaque_capable); + IF_DEF_PARAMS(ifp)->opaque_capable = OSPF_OPAQUE_CAPABLE_DEFAULT; + + IF_DEF_PARAMS(ifp)->prefix_suppression = OSPF_PREFIX_SUPPRESSION_DEFAULT; + + rc = ospf_opaque_new_if(ifp); + return rc; +} + +static int ospf_if_delete_hook(struct interface *ifp) +{ + int rc = 0; + struct route_node *rn; + struct ospf_if_info *oii; + + rc = ospf_opaque_del_if(ifp); + + /* + * This function must be called before `route_table_finish` due to + * BFD integration need to iterate over the interface neighbors to + * remove all registrations. + */ + ospf_del_if_params(ifp, IF_DEF_PARAMS(ifp)); + + for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn)) + if (rn->info) + ospf_del_if_params(ifp, rn->info); + + route_table_finish(IF_OIFS(ifp)); + route_table_finish(IF_OIFS_PARAMS(ifp)); + + /* Close per-interface socket */ + oii = ifp->info; + if (oii && oii->oii_fd > 0) { + close(oii->oii_fd); + oii->oii_fd = -1; + } + + XFREE(MTYPE_OSPF_IF_INFO, ifp->info); + + return rc; +} + +int ospf_if_is_enable(struct ospf_interface *oi) +{ + if (!(if_is_loopback(oi->ifp))) + if (if_is_up(oi->ifp)) + return 1; + + return 0; +} + +void ospf_if_set_multicast(struct ospf_interface *oi) +{ + if ((oi->state > ISM_Loopback) && (oi->type != OSPF_IFTYPE_LOOPBACK) + && (oi->type != OSPF_IFTYPE_VIRTUALLINK) + && (OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_ACTIVE)) { + /* The interface should belong to the OSPF-all-routers group. */ + if (!OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS) + && (ospf_if_add_allspfrouters(oi->ospf, oi->address, + oi->ifp->ifindex) + >= 0)) + /* Set the flag only if the system call to join + * succeeded. */ + OI_MEMBER_JOINED(oi, MEMBER_ALLROUTERS); + } else { + /* The interface should NOT belong to the OSPF-all-routers + * group. */ + if (OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS)) { + /* Only actually drop if this is the last reference */ + if (OI_MEMBER_COUNT(oi, MEMBER_ALLROUTERS) == 1) + ospf_if_drop_allspfrouters(oi->ospf, + oi->address, + oi->ifp->ifindex); + /* Unset the flag regardless of whether the system call + to leave + the group succeeded, since it's much safer to assume + that + we are not a member. */ + OI_MEMBER_LEFT(oi, MEMBER_ALLROUTERS); + } + } + + if (((oi->type == OSPF_IFTYPE_BROADCAST) + || (oi->type == OSPF_IFTYPE_POINTOPOINT)) + && ((oi->state == ISM_DR) || (oi->state == ISM_Backup)) + && (OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_ACTIVE)) { + /* The interface should belong to the OSPF-designated-routers + * group. */ + if (!OI_MEMBER_CHECK(oi, MEMBER_DROUTERS) + && (ospf_if_add_alldrouters(oi->ospf, oi->address, + oi->ifp->ifindex) + >= 0)) + /* Set the flag only if the system call to join + * succeeded. */ + OI_MEMBER_JOINED(oi, MEMBER_DROUTERS); + } else { + /* The interface should NOT belong to the + * OSPF-designated-routers group */ + if (OI_MEMBER_CHECK(oi, MEMBER_DROUTERS)) { + /* drop only if last reference */ + if (OI_MEMBER_COUNT(oi, MEMBER_DROUTERS) == 1) + ospf_if_drop_alldrouters(oi->ospf, oi->address, + oi->ifp->ifindex); + + /* Unset the flag regardless of whether the system call + to leave + the group succeeded, since it's much safer to assume + that + we are not a member. */ + OI_MEMBER_LEFT(oi, MEMBER_DROUTERS); + } + } +} + +int ospf_if_up(struct ospf_interface *oi) +{ + if (oi == NULL) + return 0; + + if (oi->type == OSPF_IFTYPE_LOOPBACK) + OSPF_ISM_EVENT_SCHEDULE(oi, ISM_LoopInd); + else { + OSPF_ISM_EVENT_SCHEDULE(oi, ISM_InterfaceUp); + } + + return 1; +} + +int ospf_if_down(struct ospf_interface *oi) +{ + struct ospf *ospf; + struct route_node *rn; + struct ospf_route *or; + struct listnode *nh; + struct ospf_path *op; + + if (oi == NULL) + return 0; + + ospf = oi->ospf; + + /* Cease the HELPER role for all the neighbours + * of this interface. + */ + if (ospf->is_helper_supported) { + struct route_node *rn = NULL; + + if (ospf_interface_neighbor_count(oi)) { + for (rn = route_top(oi->nbrs); rn; + rn = route_next(rn)) { + struct ospf_neighbor *nbr = NULL; + + if (!rn->info) + continue; + + nbr = rn->info; + + if (OSPF_GR_IS_ACTIVE_HELPER(nbr)) + ospf_gr_helper_exit( + nbr, OSPF_GR_HELPER_TOPO_CHG); + } + } + } + + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown); + /* delete position in router LSA */ + oi->lsa_pos_beg = 0; + oi->lsa_pos_end = 0; + /* Shutdown packet reception and sending */ + ospf_if_stream_unset(oi); + + if (!ospf->new_table) + return 1; + for (rn = route_top(ospf->new_table); rn; rn = route_next(rn)) { + or = rn->info; + + if (!or) + continue; + + for (nh = listhead(or->paths); nh; + nh = listnextnode_unchecked(nh)) { + op = listgetdata(nh); + if (op->ifindex == oi->ifp->ifindex) { + or->changed = true; + break; + } + } + } + + return 1; +} + + +/* Virtual Link related functions. */ + +struct ospf_vl_data *ospf_vl_data_new(struct ospf_area *area, + struct in_addr vl_peer) +{ + struct ospf_vl_data *vl_data; + + vl_data = XCALLOC(MTYPE_OSPF_VL_DATA, sizeof(struct ospf_vl_data)); + + vl_data->vl_peer.s_addr = vl_peer.s_addr; + vl_data->vl_area_id = area->area_id; + vl_data->vl_area_id_fmt = area->area_id_fmt; + + return vl_data; +} + +void ospf_vl_data_free(struct ospf_vl_data *vl_data) +{ + XFREE(MTYPE_OSPF_VL_DATA, vl_data); +} + +unsigned int vlink_count = 0; + +struct ospf_interface *ospf_vl_new(struct ospf *ospf, + struct ospf_vl_data *vl_data) +{ + struct ospf_interface *voi; + struct interface *vi; + char ifname[INTERFACE_NAMSIZ]; + struct ospf_area *area; + struct in_addr area_id; + struct connected *co; + struct prefix_ipv4 *p; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: (%s): Start", __func__, ospf_get_name(ospf)); + if (vlink_count == OSPF_VL_MAX_COUNT) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: Alarm: cannot create more than OSPF_MAX_VL_COUNT virtual links", + __func__); + + return NULL; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: creating pseudo zebra interface vrf id %u", + __func__, ospf->vrf_id); + + snprintf(ifname, sizeof(ifname), "VLINK%u", vlink_count); + vi = if_get_by_name(ifname, ospf->vrf_id, ospf->name); + /* + * if_get_by_name sets ZEBRA_INTERFACE_LINKDETECTION + * virtual links don't need this. + */ + UNSET_FLAG(vi->status, ZEBRA_INTERFACE_LINKDETECTION); + co = connected_new(); + co->ifp = vi; + listnode_add(vi->connected, co); + + p = prefix_ipv4_new(); + p->family = AF_INET; + p->prefix.s_addr = INADDR_ANY; + p->prefixlen = 0; + + co->address = (struct prefix *)p; + + voi = ospf_if_new(ospf, vi, co->address); + if (voi == NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: Alarm: OSPF int structure is not created", + __func__); + + return NULL; + } + voi->connected = co; + voi->vl_data = vl_data; + voi->ifp->mtu = OSPF_VL_MTU; + voi->type = OSPF_IFTYPE_VIRTUALLINK; + + vlink_count++; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Created name: %s set if->name to %s", __func__, + ifname, vi->name); + + area_id.s_addr = INADDR_ANY; + area = ospf_area_get(ospf, area_id); + voi->area = area; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: set associated area to the backbone", __func__); + + /* Add pseudo neighbor. */ + ospf_nbr_self_reset(voi, voi->ospf->router_id); + + ospf_area_add_if(voi->area, voi); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Stop", __func__); + return voi; +} + +static void ospf_vl_if_delete(struct ospf_vl_data *vl_data) +{ + struct interface *ifp = vl_data->vl_oi->ifp; + struct vrf *vrf = ifp->vrf; + + vl_data->vl_oi->address->u.prefix4.s_addr = INADDR_ANY; + vl_data->vl_oi->address->prefixlen = 0; + ospf_if_free(vl_data->vl_oi); + if_delete(&ifp); + if (!vrf_is_enabled(vrf)) + vrf_delete(vrf); +} + +/* for a defined area, count the number of configured vl + */ +int ospf_vl_count(struct ospf *ospf, struct ospf_area *area) +{ + int count = 0; + struct ospf_vl_data *vl_data; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(ospf->vlinks, node, vl_data)) { + if (area + && !IPV4_ADDR_SAME(&vl_data->vl_area_id, &area->area_id)) + continue; + count++; + } + return count; +} + +/* Look up vl_data for given peer, optionally qualified to be in the + * specified area. NULL area returns first found.. + */ +struct ospf_vl_data *ospf_vl_lookup(struct ospf *ospf, struct ospf_area *area, + struct in_addr vl_peer) +{ + struct ospf_vl_data *vl_data; + struct listnode *node; + + if (IS_DEBUG_OSPF_EVENT) { + zlog_debug("%s: Looking for %pI4", __func__, &vl_peer); + if (area) + zlog_debug("%s: in area %pI4", __func__, + &area->area_id); + } + + for (ALL_LIST_ELEMENTS_RO(ospf->vlinks, node, vl_data)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: VL %s, peer %pI4", __func__, + vl_data->vl_oi->ifp->name, + &vl_data->vl_peer); + + if (area + && !IPV4_ADDR_SAME(&vl_data->vl_area_id, &area->area_id)) + continue; + + if (IPV4_ADDR_SAME(&vl_data->vl_peer, &vl_peer)) + return vl_data; + } + + return NULL; +} + +static void ospf_vl_shutdown(struct ospf_vl_data *vl_data) +{ + struct ospf_interface *oi; + + if ((oi = vl_data->vl_oi) == NULL) + return; + + oi->address->u.prefix4.s_addr = INADDR_ANY; + oi->address->prefixlen = 0; + + UNSET_FLAG(oi->ifp->flags, IFF_UP); + /* OSPF_ISM_EVENT_SCHEDULE (oi, ISM_InterfaceDown); */ + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown); +} + +void ospf_vl_add(struct ospf *ospf, struct ospf_vl_data *vl_data) +{ + listnode_add(ospf->vlinks, vl_data); + hook_call(ospf_vl_add, vl_data); +} + +void ospf_vl_delete(struct ospf *ospf, struct ospf_vl_data *vl_data) +{ + ospf_vl_shutdown(vl_data); + ospf_vl_if_delete(vl_data); + + hook_call(ospf_vl_delete, vl_data); + listnode_delete(ospf->vlinks, vl_data); + + ospf_vl_data_free(vl_data); +} + +static int ospf_vl_set_params(struct ospf_area *area, + struct ospf_vl_data *vl_data, struct vertex *v) +{ + int changed = 0; + struct ospf_interface *voi; + struct listnode *node; + struct vertex_parent *vp = NULL; + unsigned int i; + struct router_lsa *rl; + struct ospf_interface *oi; + + voi = vl_data->vl_oi; + + if (voi->output_cost != v->distance) { + + voi->output_cost = v->distance; + changed = 1; + } + + for (ALL_LIST_ELEMENTS_RO(v->parents, node, vp)) { + vl_data->nexthop.lsa_pos = vp->nexthop->lsa_pos; + vl_data->nexthop.router = vp->nexthop->router; + + /* + * Only deal with interface data when the local + * (calculating) node is the SPF root node + */ + if (!area->spf_dry_run) { + oi = ospf_if_lookup_by_lsa_pos( + area, vl_data->nexthop.lsa_pos); + + if (!IPV4_ADDR_SAME(&voi->address->u.prefix4, + &oi->address->u.prefix4)) + changed = 1; + + voi->address->u.prefix4 = oi->address->u.prefix4; + voi->address->prefixlen = oi->address->prefixlen; + } + + break; /* We take the first interface. */ + } + + rl = (struct router_lsa *)v->lsa; + + /* use SPF determined backlink index in struct vertex + * for virtual link destination address + */ + if (vp && vp->backlink >= 0) { + if (!IPV4_ADDR_SAME(&vl_data->peer_addr, + &rl->link[vp->backlink].link_data)) + changed = 1; + vl_data->peer_addr = rl->link[vp->backlink].link_data; + } else { + /* This is highly odd, there is no backlink index + * there should be due to the ospf_spf_has_link() check + * in SPF. Lets warn and try pick a link anyway. + */ + zlog_info("ospf_vl_set_params: No backlink for %s!", + vl_data->vl_oi->ifp->name); + for (i = 0; i < ntohs(rl->links); i++) { + switch (rl->link[i].type) { + case LSA_LINK_TYPE_VIRTUALLINK: + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "found back link through VL"); + /* fallthru */ + case LSA_LINK_TYPE_TRANSIT: + case LSA_LINK_TYPE_POINTOPOINT: + if (!IPV4_ADDR_SAME(&vl_data->peer_addr, + &rl->link[i].link_data)) + changed = 1; + vl_data->peer_addr = rl->link[i].link_data; + } + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: %s peer address: %pI4, cost: %d,%schanged", + __func__, vl_data->vl_oi->ifp->name, + &vl_data->peer_addr, voi->output_cost, + (changed ? " " : " un")); + + return changed; +} + + +void ospf_vl_up_check(struct ospf_area *area, struct in_addr rid, + struct vertex *v) +{ + struct ospf *ospf = area->ospf; + struct listnode *node; + struct ospf_vl_data *vl_data; + struct ospf_interface *oi; + + if (IS_DEBUG_OSPF_EVENT) { + zlog_debug("%s: Start", __func__); + zlog_debug("%s: Router ID is %pI4 Area is %pI4", __func__, &rid, + &area->area_id); + } + + for (ALL_LIST_ELEMENTS_RO(ospf->vlinks, node, vl_data)) { + if (IS_DEBUG_OSPF_EVENT) { + zlog_debug("%s: considering VL, %s in area %pI4", + __func__, vl_data->vl_oi->ifp->name, + &vl_data->vl_area_id); + zlog_debug("%s: peer ID: %pI4", __func__, + &vl_data->vl_peer); + } + + if (IPV4_ADDR_SAME(&vl_data->vl_peer, &rid) + && IPV4_ADDR_SAME(&vl_data->vl_area_id, &area->area_id)) { + oi = vl_data->vl_oi; + SET_FLAG(vl_data->flags, OSPF_VL_FLAG_APPROVED); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: this VL matched", __func__); + + if (oi->state == ISM_Down) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: VL is down, waking it up", + __func__); + SET_FLAG(oi->ifp->flags, IFF_UP); + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp); + } + + if (ospf_vl_set_params(area, vl_data, v)) { + if (IS_DEBUG_OSPF(ism, ISM_EVENTS)) + zlog_debug( + "%s: VL cost change, scheduling router lsa refresh", + __func__); + if (ospf->backbone) + ospf_router_lsa_update_area( + ospf->backbone); + else if (IS_DEBUG_OSPF(ism, ISM_EVENTS)) + zlog_debug( + "%s: VL cost change, no backbone!", + __func__); + } + } + } +} + +void ospf_vl_unapprove(struct ospf *ospf) +{ + struct listnode *node; + struct ospf_vl_data *vl_data; + + for (ALL_LIST_ELEMENTS_RO(ospf->vlinks, node, vl_data)) + UNSET_FLAG(vl_data->flags, OSPF_VL_FLAG_APPROVED); +} + +void ospf_vl_shut_unapproved(struct ospf *ospf) +{ + struct listnode *node, *nnode; + struct ospf_vl_data *vl_data; + + for (ALL_LIST_ELEMENTS(ospf->vlinks, node, nnode, vl_data)) + if (!CHECK_FLAG(vl_data->flags, OSPF_VL_FLAG_APPROVED)) + ospf_vl_shutdown(vl_data); +} + +int ospf_full_virtual_nbrs(struct ospf_area *area) +{ + if (IS_DEBUG_OSPF_EVENT) { + zlog_debug( + "counting fully adjacent virtual neighbors in area %pI4", + &area->area_id); + zlog_debug("there are %d of them", area->full_vls); + } + + return area->full_vls; +} + +int ospf_vls_in_area(struct ospf_area *area) +{ + struct listnode *node; + struct ospf_vl_data *vl_data; + int c = 0; + + for (ALL_LIST_ELEMENTS_RO(area->ospf->vlinks, node, vl_data)) + if (IPV4_ADDR_SAME(&vl_data->vl_area_id, &area->area_id)) + c++; + + return c; +} + + +struct crypt_key *ospf_crypt_key_new(void) +{ + return XCALLOC(MTYPE_OSPF_CRYPT_KEY, sizeof(struct crypt_key)); +} + +void ospf_crypt_key_add(struct list *crypt, struct crypt_key *ck) +{ + listnode_add(crypt, ck); +} + +struct crypt_key *ospf_crypt_key_lookup(struct list *auth_crypt, uint8_t key_id) +{ + struct listnode *node; + struct crypt_key *ck; + + for (ALL_LIST_ELEMENTS_RO(auth_crypt, node, ck)) + if (ck->key_id == key_id) + return ck; + + return NULL; +} + +int ospf_crypt_key_delete(struct list *auth_crypt, uint8_t key_id) +{ + struct listnode *node, *nnode; + struct crypt_key *ck; + + for (ALL_LIST_ELEMENTS(auth_crypt, node, nnode, ck)) { + if (ck->key_id == key_id) { + listnode_delete(auth_crypt, ck); + XFREE(MTYPE_OSPF_CRYPT_KEY, ck); + return 1; + } + } + + return 0; +} + +uint8_t ospf_default_iftype(struct interface *ifp) +{ + if (if_is_pointopoint(ifp)) + return OSPF_IFTYPE_POINTOPOINT; + else if (if_is_loopback(ifp)) + return OSPF_IFTYPE_LOOPBACK; + else + return OSPF_IFTYPE_BROADCAST; +} + +void ospf_if_interface(struct interface *ifp) +{ + hook_call(ospf_if_update, ifp); +} + +uint32_t ospf_if_count_area_params(struct interface *ifp) +{ + struct ospf_if_params *params; + struct route_node *rn; + uint32_t count = 0; + + params = IF_DEF_PARAMS(ifp); + if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) + count++; + + for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn)) + if ((params = rn->info) + && OSPF_IF_PARAM_CONFIGURED(params, if_area)) + count++; + + return count; +} + +static int ospf_ifp_create(struct interface *ifp) +{ + struct ospf *ospf = NULL; + struct ospf_if_info *oii; + + if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + zlog_debug( + "Zebra: interface add %s vrf %s[%u] index %d flags %llx metric %d mtu %d speed %u status 0x%x", + ifp->name, ifp->vrf->name, ifp->vrf->vrf_id, + ifp->ifindex, (unsigned long long)ifp->flags, + ifp->metric, ifp->mtu, ifp->speed, ifp->status); + + assert(ifp->info); + + oii = ifp->info; + oii->curr_mtu = ifp->mtu; + + /* Change ospf type param based on following + * condition: + * ospf type params is not set (first creation), + * OR ospf param type is changed based on + * link event, currently only handle for + * loopback interface type, for other ospf interface, + * type can be set from user config which needs to be + * preserved. + */ + if (IF_DEF_PARAMS(ifp) && + (!OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp), type) || + if_is_loopback(ifp))) { + SET_IF_PARAM(IF_DEF_PARAMS(ifp), type); + IF_DEF_PARAMS(ifp)->type = ospf_default_iftype(ifp); + } + + ospf = ifp->vrf->info; + if (!ospf) + return 0; + + if (ospf_if_count_area_params(ifp) > 0) + ospf_interface_area_set(ospf, ifp); + + ospf_if_recalculate_output_cost(ifp); + + ospf_if_update(ospf, ifp); + + if (HAS_LINK_PARAMS(ifp)) + ospf_mpls_te_update_if(ifp); + + hook_call(ospf_if_update, ifp); + + return 0; +} + +static int ospf_ifp_up(struct interface *ifp) +{ + struct ospf_interface *oi; + struct route_node *rn; + struct ospf_if_info *oii = ifp->info; + struct ospf *ospf; + + if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra: Interface[%s] state change to up.", + ifp->name); + + /* Open per-intf write socket if configured */ + ospf = ifp->vrf->info; + + if (ospf && ospf->oi_running && ospf->intf_socket_enabled) + ospf_ifp_sock_init(ifp); + + ospf_if_recalculate_output_cost(ifp); + + if (oii && oii->curr_mtu != ifp->mtu) { + if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + zlog_debug( + "Zebra: Interface[%s] MTU change %u -> %u.", + ifp->name, oii->curr_mtu, ifp->mtu); + + oii->curr_mtu = ifp->mtu; + /* Must reset the interface (simulate down/up) when MTU + * changes. */ + ospf_if_reset(ifp); + + return 0; + } + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + if ((oi = rn->info) == NULL) + continue; + + ospf_if_up(oi); + } + + if (HAS_LINK_PARAMS(ifp)) + ospf_mpls_te_update_if(ifp); + + return 0; +} + +static int ospf_ifp_down(struct interface *ifp) +{ + struct ospf_interface *oi; + struct route_node *node; + + if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra: Interface[%s] state change to down.", + ifp->name); + + for (node = route_top(IF_OIFS(ifp)); node; node = route_next(node)) { + if ((oi = node->info) == NULL) + continue; + ospf_if_down(oi); + } + + /* Close per-interface write socket if configured */ + ospf_ifp_sock_close(ifp); + + return 0; +} + +static int ospf_ifp_destroy(struct interface *ifp) +{ + struct ospf *ospf; + struct route_node *rn; + + if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + zlog_debug( + "Zebra: interface delete %s vrf %s[%u] index %d flags %llx metric %d mtu %d", + ifp->name, ifp->vrf->name, ifp->vrf->vrf_id, + ifp->ifindex, (unsigned long long)ifp->flags, + ifp->metric, ifp->mtu); + + hook_call(ospf_if_delete, ifp); + + ospf = ifp->vrf->info; + if (ospf) { + if (ospf_if_count_area_params(ifp) > 0) + ospf_interface_area_unset(ospf, ifp); + } + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) + if (rn->info) + ospf_if_free((struct ospf_interface *)rn->info); + + return 0; +} + +/* Resetting ospf hello timer */ +void ospf_reset_hello_timer(struct interface *ifp, struct in_addr addr, + bool is_addr) +{ + struct route_node *rn; + + if (is_addr) { + struct prefix p; + struct ospf_interface *oi = NULL; + + p.u.prefix4 = addr; + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + + oi = ospf_if_table_lookup(ifp, &p); + + if (oi) { + /* Send hello before restart the hello timer + * to avoid session flaps in case of bigger + * hello interval configurations. + */ + ospf_hello_send(oi); + + /* Restart hello timer for this interface */ + EVENT_OFF(oi->t_hello); + OSPF_HELLO_TIMER_ON(oi); + } + + return; + } + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi = rn->info; + + if (!oi) + continue; + + /* If hello interval configured on this oi, don't restart. */ + if (OSPF_IF_PARAM_CONFIGURED(oi->params, v_hello)) + continue; + + /* Send hello before restart the hello timer + * to avoid session flaps in case of bigger + * hello interval configurations. + */ + ospf_hello_send(oi); + + /* Restart the hello timer. */ + EVENT_OFF(oi->t_hello); + OSPF_HELLO_TIMER_ON(oi); + } +} + +void ospf_if_init(void) +{ + if_zapi_callbacks(ospf_ifp_create, ospf_ifp_up, + ospf_ifp_down, ospf_ifp_destroy); + + /* Initialize Zebra interface data structure. */ + hook_register_prio(if_add, 0, ospf_if_new_hook); + hook_register_prio(if_del, 0, ospf_if_delete_hook); +} diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h new file mode 100644 index 0000000..e2290a8 --- /dev/null +++ b/ospfd/ospf_interface.h @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF Interface functions. + * Copyright (C) 1999 Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPF_INTERFACE_H +#define _ZEBRA_OSPF_INTERFACE_H + +#include "lib/bfd.h" +#include "qobj.h" +#include "hook.h" +#include "keychain.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_spf.h" + +#define IF_OSPF_IF_INFO(I) ((struct ospf_if_info *)((I)->info)) +#define IF_DEF_PARAMS(I) (IF_OSPF_IF_INFO (I)->def_params) +#define IF_OIFS(I) (IF_OSPF_IF_INFO (I)->oifs) +#define IF_OIFS_PARAMS(I) (IF_OSPF_IF_INFO (I)->params) + +/* Despite the name, this macro probably is for specialist use only */ +#define OSPF_IF_PARAM_CONFIGURED(S, P) ((S) && (S)->P##__config) + +/* Test whether an OSPF interface parameter is set, generally, given some + * existing ospf interface + */ +#define OSPF_IF_PARAM_IS_SET(O, P) \ + (OSPF_IF_PARAM_CONFIGURED((O)->params, P) \ + || OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS((O)->ifp)->P)) + +#define OSPF_IF_PARAM(O, P) \ + (OSPF_IF_PARAM_CONFIGURED((O)->params, P) \ + ? (O)->params->P \ + : IF_DEF_PARAMS((O)->ifp)->P) + +#define DECLARE_IF_PARAM(T, P) \ + T P; \ + uint8_t P##__config : 1 +#define UNSET_IF_PARAM(S, P) ((S)->P##__config) = 0 +#define SET_IF_PARAM(S, P) ((S)->P##__config) = 1 + +struct ospf_if_params { + DECLARE_IF_PARAM(uint32_t, + transmit_delay); /* Interface Transmisson Delay */ + DECLARE_IF_PARAM(uint32_t, + output_cost_cmd); /* Command Interface Output Cost */ + DECLARE_IF_PARAM(uint32_t, + retransmit_interval); /* Retransmission Interval */ + DECLARE_IF_PARAM(uint8_t, passive_interface); /* OSPF Interface is + passive: no sending or + receiving (no need to + join multicast groups) + */ + DECLARE_IF_PARAM(uint8_t, priority); /* OSPF Interface priority */ + /* Enable OSPF on this interface with area if_area */ + DECLARE_IF_PARAM(struct in_addr, if_area); + uint32_t if_area_id_fmt; + + DECLARE_IF_PARAM(uint8_t, type); /* type of interface */ +#define OSPF_IF_ACTIVE 0 +#define OSPF_IF_PASSIVE 1 + +#define OSPF_IF_PASSIVE_STATUS(O) \ + (OSPF_IF_PARAM_CONFIGURED((O)->params, passive_interface) \ + ? (O)->params->passive_interface \ + : (OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS((O)->ifp), \ + passive_interface) \ + ? IF_DEF_PARAMS((O)->ifp)->passive_interface \ + : (O)->ospf->passive_interface_default)) + + DECLARE_IF_PARAM(uint32_t, v_hello); /* Hello Interval */ + DECLARE_IF_PARAM(uint32_t, v_wait); /* Router Dead Interval */ + bool is_v_wait_set; /* Check for Dead Interval set */ + + /* GR Hello Delay Interval */ + DECLARE_IF_PARAM(uint16_t, v_gr_hello_delay); + + /* MTU mismatch check (see RFC2328, chap 10.6) */ + DECLARE_IF_PARAM(uint8_t, mtu_ignore); + + /* Fast-Hellos */ + DECLARE_IF_PARAM(uint8_t, fast_hello); + + /* Prefix-Suppression */ + DECLARE_IF_PARAM(bool, prefix_suppression); + + /* Authentication data. */ + uint8_t auth_simple[OSPF_AUTH_SIMPLE_SIZE + 1]; /* Simple password. */ + uint8_t auth_simple__config : 1; + + DECLARE_IF_PARAM(struct list *, + auth_crypt); /* List of Auth cryptographic data. */ + DECLARE_IF_PARAM(int, auth_type); /* OSPF authentication type */ + + DECLARE_IF_PARAM(char*, keychain_name); /* OSPF HMAC Cryptographic Authentication*/ + + /* Other, non-configuration state */ + uint32_t network_lsa_seqnum; /* Network LSA seqnum */ + + /* BFD configuration */ + struct bfd_configuration { + /** BFD session detection multiplier. */ + uint8_t detection_multiplier; + /** BFD session minimum required receive interval. */ + uint32_t min_rx; + /** BFD session minimum required transmission interval. */ + uint32_t min_tx; + /** BFD profile. */ + char profile[BFD_PROFILE_NAME_LEN]; + } *bfd_config; + + /* MPLS LDP-IGP Sync configuration */ + struct ldp_sync_info *ldp_sync_info; + + /* point-to-point DMVPN configuration */ + uint8_t ptp_dmvpn; + + /* point-to-multipoint delayed reflooding configuration */ + bool p2mp_delay_reflood; + + /* Opaque LSA capability at interface level (see RFC5250) */ + DECLARE_IF_PARAM(bool, opaque_capable); +}; + +enum { MEMBER_ALLROUTERS = 0, + MEMBER_DROUTERS, + MEMBER_MAX, +}; + +struct ospf_if_info { + struct ospf_if_params *def_params; + struct route_table *params; + struct route_table *oifs; + unsigned int + membership_counts[MEMBER_MAX]; /* multicast group refcnts */ + + uint32_t curr_mtu; + + /* Per-interface write socket, configured via 'ospf' object */ + int oii_fd; +}; + +struct ospf_interface; + +struct ospf_vl_data { + struct in_addr vl_peer; /* Router-ID of the peer */ + struct in_addr vl_area_id; /* Transit area */ + int vl_area_id_fmt; /* Area ID format */ + struct ospf_interface *vl_oi; /* Interface data structure */ + struct vertex_nexthop nexthop; /* Nexthop router and oi to use */ + struct in_addr peer_addr; /* Address used to reach the peer */ + uint8_t flags; +}; + + +#define OSPF_VL_MAX_COUNT 256 +#define OSPF_VL_MTU 1500 + +#define OSPF_VL_FLAG_APPROVED 0x01 + +struct crypt_key { + uint8_t key_id; + uint8_t auth_key[OSPF_AUTH_MD5_SIZE + 1]; +}; + +/* OSPF interface structure. */ +struct ospf_interface { + /* This interface's parent ospf instance. */ + struct ospf *ospf; + + /* OSPF Area. */ + struct ospf_area *area; + + /* Position range in Router LSA */ + uint16_t lsa_pos_beg; /* inclusive, >= */ + uint16_t lsa_pos_end; /* exclusive, < */ + + /* Interface data from zebra. */ + struct interface *ifp; + struct ospf_vl_data *vl_data; /* Data for Virtual Link */ + + /* Packet send buffer. */ + struct ospf_fifo *obuf; /* Output queue */ + + /* OSPF Network Type. */ + uint8_t type; + + /* point-to-point DMVPN configuration */ + uint8_t ptp_dmvpn; + + /* point-to-multipoint delayed reflooding */ + bool p2mp_delay_reflood; + + /* State of Interface State Machine. */ + uint8_t state; + + /* To which multicast groups do we currently belong? */ + uint8_t multicast_memberships; +#define OI_MEMBER_FLAG(M) (1 << (M)) +#define OI_MEMBER_COUNT(O,M) (IF_OSPF_IF_INFO(oi->ifp)->membership_counts[(M)]) +#define OI_MEMBER_CHECK(O, M) \ + (CHECK_FLAG((O)->multicast_memberships, OI_MEMBER_FLAG(M))) +#define OI_MEMBER_JOINED(O, M) \ + do { \ + SET_FLAG((O)->multicast_memberships, OI_MEMBER_FLAG(M)); \ + IF_OSPF_IF_INFO((O)->ifp)->membership_counts[(M)]++; \ + } while (0) +#define OI_MEMBER_LEFT(O, M) \ + do { \ + UNSET_FLAG((O)->multicast_memberships, OI_MEMBER_FLAG(M)); \ + IF_OSPF_IF_INFO((O)->ifp)->membership_counts[(M)]--; \ + } while (0) + + struct prefix *address; /* Interface prefix */ + struct connected *connected; /* Pointer to connected */ + + /* Configured varables. */ + struct ospf_if_params *params; + + uint32_t crypt_seqnum; /* Cryptographic Sequence Number */ + uint32_t output_cost; /* Acutual Interface Output Cost */ + + /* Neighbor information. */ + struct route_table *nbrs; /* OSPF Neighbor List */ + struct ospf_neighbor *nbr_self; /* Neighbor Self */ +#define DR(I) ((I)->nbr_self->d_router) +#define BDR(I) ((I)->nbr_self->bd_router) +#define OPTIONS(I) ((I)->nbr_self->options) +#define PRIORITY(I) ((I)->nbr_self->priority) + + /* List of configured NBMA neighbor. */ + struct list *nbr_nbma; + + /* Graceful-Restart data. */ + struct { + struct { + uint16_t elapsed_seconds; + struct event *t_grace_send; + } hello_delay; + } gr; + + /* self-originated LSAs. */ + struct ospf_lsa *network_lsa_self; /* network-LSA. */ + struct list *opaque_lsa_self; /* Type-9 Opaque-LSAs */ + + struct route_table *ls_upd_queue; + + struct list *ls_ack; /* Link State Acknowledgment list. */ + + struct { + struct list *ls_ack; + struct in_addr dst; + } ls_ack_direct; + + /* Timer values. */ + uint32_t v_ls_ack; /* Delayed Link State Acknowledgment */ + + /* Threads. */ + struct event *t_hello; /* timer */ + struct event *t_wait; /* timer */ + struct event *t_ls_ack; /* timer */ + struct event *t_ls_ack_direct; /* event */ + struct event *t_ls_upd_event; /* event */ + struct event *t_opaque_lsa_self; /* Type-9 Opaque-LSAs */ + + int on_write_q; + + /* Statistics fields. */ + uint32_t hello_in; /* Hello message input count. */ + uint32_t hello_out; /* Hello message output count. */ + uint32_t db_desc_in; /* database desc. message input count. */ + uint32_t db_desc_out; /* database desc. message output count. */ + uint32_t ls_req_in; /* LS request message input count. */ + uint32_t ls_req_out; /* LS request message output count. */ + uint32_t ls_upd_in; /* LS update message input count. */ + uint32_t ls_upd_out; /* LS update message output count. */ + uint32_t ls_ack_in; /* LS Ack message input count. */ + uint32_t ls_ack_out; /* LS Ack message output count. */ + uint32_t discarded; /* discarded input count by error. */ + uint32_t state_change; /* Number of status change. */ + + uint32_t full_nbrs; + + /* Buffered values for keychain and key */ + struct keychain *keychain; + struct key *key; + + QOBJ_FIELDS; +}; +DECLARE_QOBJ_TYPE(ospf_interface); + +/* Prototypes. */ +extern char *ospf_if_name(struct ospf_interface *oi); +extern struct ospf_interface * +ospf_if_new(struct ospf *ospf, struct interface *ifp, struct prefix *p); +extern void ospf_if_cleanup(struct ospf_interface *oi); +extern void ospf_if_free(struct ospf_interface *oi); +extern int ospf_if_up(struct ospf_interface *oi); +extern int ospf_if_down(struct ospf_interface *oi); + +extern int ospf_if_is_up(struct ospf_interface *oi); +extern struct ospf_interface *ospf_if_exists(struct ospf_interface *oi); +extern struct ospf_interface *ospf_if_lookup_by_lsa_pos(struct ospf_area *area, + int lsa_pos); +extern struct ospf_interface * +ospf_if_lookup_by_local_addr(struct ospf *ospf, struct interface *ifp, + struct in_addr addr); +extern struct ospf_interface *ospf_if_lookup_by_prefix(struct ospf *ospf, + struct prefix_ipv4 *p); +extern struct ospf_interface *ospf_if_table_lookup(struct interface *ifp, + struct prefix *p); +extern struct ospf_interface *ospf_if_addr_local(struct in_addr addr); +extern struct ospf_interface *ospf_if_lookup_recv_if(struct ospf *ospf, + struct in_addr addr, + struct interface *ifp); +extern struct ospf_interface *ospf_if_is_configured(struct ospf *ospf, + struct in_addr *addr); + +extern struct ospf_if_params *ospf_lookup_if_params(struct interface *ifp, + struct in_addr addr); +extern struct ospf_if_params *ospf_get_if_params(struct interface *ifp, + struct in_addr addr); +extern void ospf_free_if_params(struct interface *ifp, struct in_addr addr); +extern void ospf_if_update_params(struct interface *ifp, struct in_addr addr); + +extern int ospf_if_new_hook(struct interface *ifp); +extern void ospf_if_init(void); +extern void ospf_if_stream_unset(struct ospf_interface *oi); +extern void ospf_if_reset_variables(struct ospf_interface *oi); +extern int ospf_if_is_enable(struct ospf_interface *oi); +extern int ospf_if_get_output_cost(struct ospf_interface *oi); +extern void ospf_if_recalculate_output_cost(struct interface *ifp); + +/* Simulate down/up on the interface. */ +extern void ospf_if_reset(struct interface *ifp); + +extern struct ospf_interface *ospf_vl_new(struct ospf *ospf, + struct ospf_vl_data *vl_data); +extern struct ospf_vl_data *ospf_vl_data_new(struct ospf_area *area, + struct in_addr addr); +extern struct ospf_vl_data * +ospf_vl_lookup(struct ospf *ospf, struct ospf_area *area, struct in_addr addr); +extern int ospf_vl_count(struct ospf *ospf, struct ospf_area *area); +extern void ospf_vl_data_free(struct ospf_vl_data *vl_data); +extern void ospf_vl_add(struct ospf *ospf, struct ospf_vl_data *vl_data); +extern void ospf_vl_delete(struct ospf *ospf, struct ospf_vl_data *vl_data); +extern void ospf_vl_up_check(struct ospf_area *area, struct in_addr addr, + struct vertex *vertex); +extern void ospf_vl_unapprove(struct ospf *ospf); +extern void ospf_vl_shut_unapproved(struct ospf *ospf); +extern int ospf_full_virtual_nbrs(struct ospf_area *area); +extern int ospf_vls_in_area(struct ospf_area *area); + +extern struct crypt_key *ospf_crypt_key_lookup(struct list *list, + uint8_t key_id); +extern struct crypt_key *ospf_crypt_key_new(void); +extern void ospf_crypt_key_add(struct list *list, struct crypt_key *key); +extern int ospf_crypt_key_delete(struct list *list, uint8_t key_id); +extern uint8_t ospf_default_iftype(struct interface *ifp); +extern int ospf_interface_neighbor_count(struct ospf_interface *oi); + +/* Set all multicast memberships appropriately based on the type and + state of the interface. */ +extern void ospf_if_set_multicast(struct ospf_interface *oi); + +extern void ospf_if_interface(struct interface *ifp); + +extern uint32_t ospf_if_count_area_params(struct interface *ifp); +extern void ospf_reset_hello_timer(struct interface *ifp, struct in_addr addr, + bool is_addr); + +extern void ospf_interface_fifo_flush(struct ospf_interface *oi); +DECLARE_HOOK(ospf_vl_add, (struct ospf_vl_data * vd), (vd)); +DECLARE_HOOK(ospf_vl_delete, (struct ospf_vl_data * vd), (vd)); + +DECLARE_HOOK(ospf_if_update, (struct interface * ifp), (ifp)); +DECLARE_HOOK(ospf_if_delete, (struct interface * ifp), (ifp)); + +#endif /* _ZEBRA_OSPF_INTERFACE_H */ diff --git a/ospfd/ospf_ism.c b/ospfd/ospf_ism.c new file mode 100644 index 0000000..2516fa7 --- /dev/null +++ b/ospfd/ospf_ism.c @@ -0,0 +1,595 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF version 2 Interface State Machine + * From RFC2328 [OSPF Version 2] + * Copyright (C) 1999, 2000 Toshiaki Takada + */ + +#include + +#include "frrevent.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "log.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_network.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_abr.h" + +DEFINE_HOOK(ospf_ism_change, + (struct ospf_interface * oi, int state, int oldstate), + (oi, state, oldstate)); + +/* elect DR and BDR. Refer to RFC2319 section 9.4 */ +static struct ospf_neighbor *ospf_dr_election_sub(struct list *routers) +{ + struct listnode *node; + struct ospf_neighbor *nbr, *max = NULL; + + /* Choose highest router priority. + In case of tie, choose highest Router ID. */ + for (ALL_LIST_ELEMENTS_RO(routers, node, nbr)) { + if (max == NULL) + max = nbr; + else { + if (max->priority < nbr->priority) + max = nbr; + else if (max->priority == nbr->priority) + if (IPV4_ADDR_CMP(&max->router_id, + &nbr->router_id) + < 0) + max = nbr; + } + } + + return max; +} + +static struct ospf_neighbor *ospf_elect_dr(struct ospf_interface *oi, + struct list *el_list) +{ + struct list *dr_list; + struct listnode *node; + struct ospf_neighbor *nbr, *dr = NULL, *bdr = NULL; + + dr_list = list_new(); + + /* Add neighbors to the list. */ + for (ALL_LIST_ELEMENTS_RO(el_list, node, nbr)) { + /* neighbor declared to be DR. */ + if (NBR_IS_DR(nbr)) + listnode_add(dr_list, nbr); + + /* Preserve neighbor BDR. */ + if (IPV4_ADDR_SAME(&BDR(oi), &nbr->address.u.prefix4)) + bdr = nbr; + } + + /* Elect Designated Router. */ + if (listcount(dr_list) > 0) + dr = ospf_dr_election_sub(dr_list); + else + dr = bdr; + + /* Set DR to interface. */ + if (dr) + DR(oi) = dr->address.u.prefix4; + else + DR(oi).s_addr = 0; + + list_delete(&dr_list); + + return dr; +} + +static struct ospf_neighbor *ospf_elect_bdr(struct ospf_interface *oi, + struct list *el_list) +{ + struct list *bdr_list, *no_dr_list; + struct listnode *node; + struct ospf_neighbor *nbr, *bdr = NULL; + + bdr_list = list_new(); + no_dr_list = list_new(); + + /* Add neighbors to the list. */ + for (ALL_LIST_ELEMENTS_RO(el_list, node, nbr)) { + /* neighbor declared to be DR. */ + if (NBR_IS_DR(nbr)) + continue; + + /* neighbor declared to be BDR. */ + if (NBR_IS_BDR(nbr)) + listnode_add(bdr_list, nbr); + + listnode_add(no_dr_list, nbr); + } + + /* Elect Backup Designated Router. */ + if (listcount(bdr_list) > 0) + bdr = ospf_dr_election_sub(bdr_list); + else + bdr = ospf_dr_election_sub(no_dr_list); + + /* Set BDR to interface. */ + if (bdr) + BDR(oi) = bdr->address.u.prefix4; + else + BDR(oi).s_addr = 0; + + list_delete(&bdr_list); + list_delete(&no_dr_list); + + return bdr; +} + +static int ospf_ism_state(struct ospf_interface *oi) +{ + if (IPV4_ADDR_SAME(&DR(oi), &oi->address->u.prefix4)) + return ISM_DR; + else if (IPV4_ADDR_SAME(&BDR(oi), &oi->address->u.prefix4)) + return ISM_Backup; + else + return ISM_DROther; +} + +static void ospf_dr_eligible_routers(struct route_table *nbrs, + struct list *el_list) +{ + struct route_node *rn; + struct ospf_neighbor *nbr; + + for (rn = route_top(nbrs); rn; rn = route_next(rn)) + if ((nbr = rn->info) != NULL) + /* Ignore 0.0.0.0 node*/ + if (nbr->router_id.s_addr != INADDR_ANY) + /* Is neighbor eligible? */ + if (nbr->priority > 0) + /* Is neighbor upper 2-Way? */ + if (nbr->state >= NSM_TwoWay) + listnode_add(el_list, nbr); +} + +/* Generate AdjOK? NSM event. */ +static void ospf_dr_change(struct ospf *ospf, struct route_table *nbrs) +{ + struct route_node *rn; + struct ospf_neighbor *nbr; + + for (rn = route_top(nbrs); rn; rn = route_next(rn)) { + nbr = rn->info; + + if (!nbr) + continue; + + /* + * Ignore 0.0.0.0 node + * Is neighbor 2-Way? + * Ignore myself + */ + if (nbr->router_id.s_addr != INADDR_ANY + && nbr->state >= NSM_TwoWay + && !IPV4_ADDR_SAME(&nbr->router_id, &ospf->router_id)) + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_AdjOK); + } +} + +int ospf_dr_election(struct ospf_interface *oi) +{ + struct in_addr old_dr, old_bdr; + int old_state, new_state; + struct list *el_list; + + /* backup current values. */ + old_dr = DR(oi); + old_bdr = BDR(oi); + old_state = oi->state; + + el_list = list_new(); + + /* List eligible routers. */ + ospf_dr_eligible_routers(oi->nbrs, el_list); + + /* First election of DR and BDR. */ + ospf_elect_bdr(oi, el_list); + ospf_elect_dr(oi, el_list); + + new_state = ospf_ism_state(oi); + + if (IS_DEBUG_OSPF(ism, ISM_STATUS)) { + zlog_debug("DR-Election[1st]: Backup %pI4", &BDR(oi)); + zlog_debug("DR-Election[1st]: DR %pI4", &DR(oi)); + } + + if (new_state != old_state + && !(new_state == ISM_DROther && old_state < ISM_DROther)) { + ospf_elect_bdr(oi, el_list); + ospf_elect_dr(oi, el_list); + + new_state = ospf_ism_state(oi); + + if (IS_DEBUG_OSPF(ism, ISM_STATUS)) { + zlog_debug("DR-Election[2nd]: Backup %pI4", &BDR(oi)); + zlog_debug("DR-Election[2nd]: DR %pI4", &DR(oi)); + } + } + + list_delete(&el_list); + + /* if DR or BDR changes, cause AdjOK? neighbor event. */ + if (!IPV4_ADDR_SAME(&old_dr, &DR(oi)) + || !IPV4_ADDR_SAME(&old_bdr, &BDR(oi))) + ospf_dr_change(oi->ospf, oi->nbrs); + + return new_state; +} + + +void ospf_hello_timer(struct event *thread) +{ + struct ospf_interface *oi; + + oi = EVENT_ARG(thread); + oi->t_hello = NULL; + + /* Check if the GR hello-delay is active. */ + if (oi->gr.hello_delay.t_grace_send) + return; + + if (IS_DEBUG_OSPF(ism, ISM_TIMERS)) + zlog_debug("ISM[%s]: Timer (Hello timer expire)", IF_NAME(oi)); + + /* Sending hello packet. */ + ospf_hello_send(oi); + + /* Hello timer set. */ + OSPF_HELLO_TIMER_ON(oi); +} + +static void ospf_wait_timer(struct event *thread) +{ + struct ospf_interface *oi; + + oi = EVENT_ARG(thread); + oi->t_wait = NULL; + + if (IS_DEBUG_OSPF(ism, ISM_TIMERS)) + zlog_debug("ISM[%s]: Timer (Wait timer expire)", IF_NAME(oi)); + + OSPF_ISM_EVENT_SCHEDULE(oi, ISM_WaitTimer); +} + +/* Hook function called after ospf ISM event is occurred. And vty's + network command invoke this function after making interface + structure. */ +static void ism_timer_set(struct ospf_interface *oi) +{ + switch (oi->state) { + case ISM_Down: + /* First entry point of ospf interface state machine. In this + state + interface parameters must be set to initial values, and + timers are + reset also. */ + EVENT_OFF(oi->t_hello); + EVENT_OFF(oi->t_wait); + EVENT_OFF(oi->t_ls_ack); + EVENT_OFF(oi->gr.hello_delay.t_grace_send); + break; + case ISM_Loopback: + /* In this state, the interface may be looped back and will be + unavailable for regular data traffic. */ + EVENT_OFF(oi->t_hello); + EVENT_OFF(oi->t_wait); + EVENT_OFF(oi->t_ls_ack); + EVENT_OFF(oi->gr.hello_delay.t_grace_send); + break; + case ISM_Waiting: + /* The router is trying to determine the identity of DRouter and + BDRouter. The router begin to receive and send Hello Packets. + */ + /* send first hello immediately */ + OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, ospf_hello_timer, 1); + OSPF_ISM_TIMER_ON(oi->t_wait, ospf_wait_timer, + OSPF_IF_PARAM(oi, v_wait)); + EVENT_OFF(oi->t_ls_ack); + break; + case ISM_PointToPoint: + /* The interface connects to a physical Point-to-point network + or + virtual link. The router attempts to form an adjacency with + neighboring router. Hello packets are also sent. */ + /* send first hello immediately */ + OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, ospf_hello_timer, 1); + EVENT_OFF(oi->t_wait); + OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer, + oi->v_ls_ack); + break; + case ISM_DROther: + /* The network type of the interface is broadcast or NBMA + network, + and the router itself is neither Designated Router nor + Backup Designated Router. */ + OSPF_HELLO_TIMER_ON(oi); + EVENT_OFF(oi->t_wait); + OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer, + oi->v_ls_ack); + break; + case ISM_Backup: + /* The network type of the interface is broadcast os NBMA + network, + and the router is Backup Designated Router. */ + OSPF_HELLO_TIMER_ON(oi); + EVENT_OFF(oi->t_wait); + OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer, + oi->v_ls_ack); + break; + case ISM_DR: + /* The network type of the interface is broadcast or NBMA + network, + and the router is Designated Router. */ + OSPF_HELLO_TIMER_ON(oi); + EVENT_OFF(oi->t_wait); + OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer, + oi->v_ls_ack); + break; + } +} + +static int ism_interface_up(struct ospf_interface *oi) +{ + int next_state = 0; + + /* if network type is point-to-point, Point-to-MultiPoint or virtual + link, + the state transitions to Point-to-Point. */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT + || oi->type == OSPF_IFTYPE_POINTOMULTIPOINT + || oi->type == OSPF_IFTYPE_VIRTUALLINK) + next_state = ISM_PointToPoint; + /* Else if the router is not eligible to DR, the state transitions to + DROther. */ + else if (PRIORITY(oi) == 0) /* router is eligible? */ + next_state = ISM_DROther; + else + /* Otherwise, the state transitions to Waiting. */ + next_state = ISM_Waiting; + + if (oi->type == OSPF_IFTYPE_NBMA) + ospf_nbr_nbma_if_update(oi->ospf, oi); + + /* ospf_ism_event (t); */ + return next_state; +} + +static int ism_loop_ind(struct ospf_interface *oi) +{ + /* call ism_interface_down. */ + /* ret = ism_interface_down (oi); */ + + return 0; +} + +/* Interface down event handler. */ +static int ism_interface_down(struct ospf_interface *oi) +{ + ospf_if_cleanup(oi); + return 0; +} + + +static int ism_backup_seen(struct ospf_interface *oi) +{ + return ospf_dr_election(oi); +} + +static int ism_wait_timer(struct ospf_interface *oi) +{ + return ospf_dr_election(oi); +} + +static int ism_neighbor_change(struct ospf_interface *oi) +{ + return ospf_dr_election(oi); +} + +static int ism_ignore(struct ospf_interface *oi) +{ + if (IS_DEBUG_OSPF(ism, ISM_EVENTS)) + zlog_debug("ISM[%s]: ism_ignore called", IF_NAME(oi)); + + return 0; +} + +/* Interface State Machine */ +const struct { + int (*func)(struct ospf_interface *); + int next_state; +} ISM[OSPF_ISM_STATE_MAX][OSPF_ISM_EVENT_MAX] = { + { + /* DependUpon: dummy state. */ + {ism_ignore, ISM_DependUpon}, /* NoEvent */ + {ism_ignore, ISM_DependUpon}, /* InterfaceUp */ + {ism_ignore, ISM_DependUpon}, /* WaitTimer */ + {ism_ignore, ISM_DependUpon}, /* BackupSeen */ + {ism_ignore, ISM_DependUpon}, /* NeighborChange */ + {ism_ignore, ISM_DependUpon}, /* LoopInd */ + {ism_ignore, ISM_DependUpon}, /* UnloopInd */ + {ism_ignore, ISM_DependUpon}, /* InterfaceDown */ + }, + { + /* Down:*/ + {ism_ignore, ISM_DependUpon}, /* NoEvent */ + {ism_interface_up, ISM_DependUpon}, /* InterfaceUp */ + {ism_ignore, ISM_Down}, /* WaitTimer */ + {ism_ignore, ISM_Down}, /* BackupSeen */ + {ism_ignore, ISM_Down}, /* NeighborChange */ + {ism_loop_ind, ISM_Loopback}, /* LoopInd */ + {ism_ignore, ISM_Down}, /* UnloopInd */ + {ism_interface_down, ISM_Down}, /* InterfaceDown */ + }, + { + /* Loopback: */ + {ism_ignore, ISM_DependUpon}, /* NoEvent */ + {ism_ignore, ISM_Loopback}, /* InterfaceUp */ + {ism_ignore, ISM_Loopback}, /* WaitTimer */ + {ism_ignore, ISM_Loopback}, /* BackupSeen */ + {ism_ignore, ISM_Loopback}, /* NeighborChange */ + {ism_ignore, ISM_Loopback}, /* LoopInd */ + {ism_ignore, ISM_Down}, /* UnloopInd */ + {ism_interface_down, ISM_Down}, /* InterfaceDown */ + }, + { + /* Waiting: */ + {ism_ignore, ISM_DependUpon}, /* NoEvent */ + {ism_ignore, ISM_Waiting}, /* InterfaceUp */ + {ism_wait_timer, ISM_DependUpon}, /* WaitTimer */ + {ism_backup_seen, ISM_DependUpon}, /* BackupSeen */ + {ism_ignore, ISM_Waiting}, /* NeighborChange */ + {ism_loop_ind, ISM_Loopback}, /* LoopInd */ + {ism_ignore, ISM_Waiting}, /* UnloopInd */ + {ism_interface_down, ISM_Down}, /* InterfaceDown */ + }, + { + /* Point-to-Point: */ + {ism_ignore, ISM_DependUpon}, /* NoEvent */ + {ism_ignore, ISM_PointToPoint}, /* InterfaceUp */ + {ism_ignore, ISM_PointToPoint}, /* WaitTimer */ + {ism_ignore, ISM_PointToPoint}, /* BackupSeen */ + {ism_ignore, ISM_PointToPoint}, /* NeighborChange */ + {ism_loop_ind, ISM_Loopback}, /* LoopInd */ + {ism_ignore, ISM_PointToPoint}, /* UnloopInd */ + {ism_interface_down, ISM_Down}, /* InterfaceDown */ + }, + { + /* DROther: */ + {ism_ignore, ISM_DependUpon}, /* NoEvent */ + {ism_ignore, ISM_DROther}, /* InterfaceUp */ + {ism_ignore, ISM_DROther}, /* WaitTimer */ + {ism_ignore, ISM_DROther}, /* BackupSeen */ + {ism_neighbor_change, ISM_DependUpon}, /* NeighborChange */ + {ism_loop_ind, ISM_Loopback}, /* LoopInd */ + {ism_ignore, ISM_DROther}, /* UnloopInd */ + {ism_interface_down, ISM_Down}, /* InterfaceDown */ + }, + { + /* Backup: */ + {ism_ignore, ISM_DependUpon}, /* NoEvent */ + {ism_ignore, ISM_Backup}, /* InterfaceUp */ + {ism_ignore, ISM_Backup}, /* WaitTimer */ + {ism_ignore, ISM_Backup}, /* BackupSeen */ + {ism_neighbor_change, ISM_DependUpon}, /* NeighborChange */ + {ism_loop_ind, ISM_Loopback}, /* LoopInd */ + {ism_ignore, ISM_Backup}, /* UnloopInd */ + {ism_interface_down, ISM_Down}, /* InterfaceDown */ + }, + { + /* DR: */ + {ism_ignore, ISM_DependUpon}, /* NoEvent */ + {ism_ignore, ISM_DR}, /* InterfaceUp */ + {ism_ignore, ISM_DR}, /* WaitTimer */ + {ism_ignore, ISM_DR}, /* BackupSeen */ + {ism_neighbor_change, ISM_DependUpon}, /* NeighborChange */ + {ism_loop_ind, ISM_Loopback}, /* LoopInd */ + {ism_ignore, ISM_DR}, /* UnloopInd */ + {ism_interface_down, ISM_Down}, /* InterfaceDown */ + }, +}; + +static const char *const ospf_ism_event_str[] = { + "NoEvent", "InterfaceUp", "WaitTimer", "BackupSeen", + "NeighborChange", "LoopInd", "UnLoopInd", "InterfaceDown", +}; + +static void ism_change_state(struct ospf_interface *oi, int state) +{ + int old_state; + struct ospf_lsa *lsa; + + /* Logging change of state. */ + if (IS_DEBUG_OSPF(ism, ISM_STATUS)) + zlog_debug("ISM[%s]: State change %s -> %s", IF_NAME(oi), + lookup_msg(ospf_ism_state_msg, oi->state, NULL), + lookup_msg(ospf_ism_state_msg, state, NULL)); + + old_state = oi->state; + oi->state = state; + oi->state_change++; + + hook_call(ospf_ism_change, oi, state, old_state); + + /* Set multicast memberships appropriately for new state. */ + ospf_if_set_multicast(oi); + + if (old_state == ISM_Down || state == ISM_Down) + ospf_check_abr_status(oi->ospf); + + /* Originate router-LSA. */ + if (state == ISM_Down) { + if (oi->area->act_ints > 0) + oi->area->act_ints--; + } else if (old_state == ISM_Down) + oi->area->act_ints++; + + /* schedule router-LSA originate. */ + ospf_router_lsa_update_area(oi->area); + + /* Originate network-LSA. */ + if (old_state != ISM_DR && state == ISM_DR) + ospf_network_lsa_update(oi); + else if (old_state == ISM_DR && state != ISM_DR) { + /* Free self originated network LSA. */ + lsa = oi->network_lsa_self; + if (lsa) + ospf_lsa_flush_area(lsa, oi->area); + + ospf_lsa_unlock(&oi->network_lsa_self); + oi->network_lsa_self = NULL; + } + + ospf_opaque_ism_change(oi, old_state); + + /* Check area border status. */ + ospf_check_abr_status(oi->ospf); +} + +/* Execute ISM event process. */ +void ospf_ism_event(struct event *thread) +{ + int event; + int next_state; + struct ospf_interface *oi; + + oi = EVENT_ARG(thread); + event = EVENT_VAL(thread); + + /* Call function. */ + next_state = (*(ISM[oi->state][event].func))(oi); + + if (!next_state) + next_state = ISM[oi->state][event].next_state; + + if (IS_DEBUG_OSPF(ism, ISM_EVENTS)) + zlog_debug("ISM[%s]: %s (%s)", IF_NAME(oi), + lookup_msg(ospf_ism_state_msg, oi->state, NULL), + ospf_ism_event_str[event]); + + /* If state is changed. */ + if (next_state != oi->state) + ism_change_state(oi, next_state); + + /* Make sure timer is set. */ + ism_timer_set(oi); +} diff --git a/ospfd/ospf_ism.h b/ospfd/ospf_ism.h new file mode 100644 index 0000000..bbb059c --- /dev/null +++ b/ospfd/ospf_ism.h @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF version 2 Interface State Machine. + * From RFC2328 [OSPF Version 2] + * Copyright (C) 1999 Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPF_ISM_H +#define _ZEBRA_OSPF_ISM_H + +#include "hook.h" + +/* OSPF Interface State Machine Status. */ +#define ISM_DependUpon 0 +#define ISM_Down 1 +#define ISM_Loopback 2 +#define ISM_Waiting 3 +#define ISM_PointToPoint 4 +#define ISM_DROther 5 +#define ISM_Backup 6 +#define ISM_DR 7 +#define OSPF_ISM_STATE_MAX 8 + +/* OSPF Interface State Machine Event. */ +#define ISM_NoEvent 0 +#define ISM_InterfaceUp 1 +#define ISM_WaitTimer 2 +#define ISM_BackupSeen 3 +#define ISM_NeighborChange 4 +#define ISM_LoopInd 5 +#define ISM_UnloopInd 6 +#define ISM_InterfaceDown 7 +#define OSPF_ISM_EVENT_MAX 8 + +#define OSPF_ISM_WRITE_ON(O) \ + do { \ + if (oi->on_write_q == 0) { \ + listnode_add((O)->oi_write_q, oi); \ + oi->on_write_q = 1; \ + } \ + if (!list_isempty((O)->oi_write_q)) \ + event_add_write(master, ospf_write, (O), (O)->fd, \ + &(O)->t_write); \ + } while (0) + +/* Macro for OSPF ISM timer turn on. */ +#define OSPF_ISM_TIMER_ON(T, F, V) event_add_timer(master, (F), oi, (V), &(T)) + +#define OSPF_ISM_TIMER_MSEC_ON(T, F, V) \ + event_add_timer_msec(master, (F), oi, (V), &(T)) + +/* convenience macro to set hello timer correctly, according to + * whether fast-hello is set or not + */ +#define OSPF_HELLO_TIMER_ON(O) \ + do { \ + if (OSPF_IF_PARAM((O), fast_hello)) \ + OSPF_ISM_TIMER_MSEC_ON( \ + (O)->t_hello, ospf_hello_timer, \ + 1000 / OSPF_IF_PARAM((O), fast_hello)); \ + else \ + OSPF_ISM_TIMER_ON((O)->t_hello, ospf_hello_timer, \ + OSPF_IF_PARAM((O), v_hello)); \ + } while (0) + +/* Macro for OSPF schedule event. */ +#define OSPF_ISM_EVENT_SCHEDULE(I, E) \ + event_add_event(master, ospf_ism_event, (I), (E), NULL) + +/* Macro for OSPF execute event. */ +#define OSPF_ISM_EVENT_EXECUTE(I, E) \ + event_execute(master, ospf_ism_event, (I), (E), NULL) + +/* Prototypes. */ +extern void ospf_ism_event(struct event *thread); +extern void ism_change_status(struct ospf_interface *, int); +extern void ospf_hello_timer(struct event *thread); +extern int ospf_dr_election(struct ospf_interface *oi); + +DECLARE_HOOK(ospf_ism_change, + (struct ospf_interface * oi, int state, int oldstate), + (oi, state, oldstate)); + +#endif /* _ZEBRA_OSPF_ISM_H */ diff --git a/ospfd/ospf_ldp_sync.c b/ospfd/ospf_ldp_sync.c new file mode 100644 index 0000000..4aab880 --- /dev/null +++ b/ospfd/ospf_ldp_sync.c @@ -0,0 +1,1064 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ospf_ldp_sync.c: OSPF LDP-IGP Sync handling routines + * Copyright (C) 2020 Volta Networks, Inc. + */ + +#include +#include + +#include "monotime.h" +#include "memory.h" +#include "frrevent.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "command.h" +#include "plist.h" +#include "log.h" +#include "zclient.h" +#include +#include "defaults.h" +#include "ldp_sync.h" + +#include "ospfd.h" +#include "ospf_interface.h" +#include "ospf_vty.h" +#include "ospf_ldp_sync.h" +#include "ospf_dump.h" +#include "ospf_ism.h" + +extern struct zclient *zclient; + +/* + * LDP-SYNC msg between IGP and LDP + */ +int ospf_ldp_sync_state_update(struct ldp_igp_sync_if_state state) +{ + struct ospf *ospf; + struct interface *ifp; + + /* if ospf is not enabled or LDP-SYNC is not configured ignore */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || + !CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return 0; + + /* received ldp-sync interface state from LDP */ + ifp = if_lookup_by_index(state.ifindex, VRF_DEFAULT); + if (ifp == NULL || if_is_loopback(ifp)) + return 0; + + ols_debug("%s: rcvd %s from LDP if %s", __func__, + state.sync_start ? "sync-start" : "sync-complete", ifp->name); + if (state.sync_start) + ospf_ldp_sync_if_start(ifp, false); + else + ospf_ldp_sync_if_complete(ifp); + + return 0; +} + +int ospf_ldp_sync_announce_update(struct ldp_igp_sync_announce announce) +{ + struct ospf *ospf; + struct vrf *vrf; + struct interface *ifp; + + /* if ospf is not enabled or LDP-SYNC is not configured ignore */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || + !CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return 0; + + if (announce.proto != ZEBRA_ROUTE_LDP) + return 0; + + ols_debug("%s: rcvd announce from LDP", __func__); + + /* LDP just started up: + * set cost to LSInfinity + * send request to LDP for LDP-SYNC state for each interface + */ + vrf = vrf_lookup_by_id(ospf->vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) + ospf_ldp_sync_if_start(ifp, true); + + return 0; +} + +void ospf_ldp_sync_state_req_msg(struct interface *ifp) +{ + struct ldp_igp_sync_if_state_req request; + + ols_debug("%s: send state request to LDP for %s", __func__, ifp->name); + + memset(&request, 0, sizeof(request)); + strlcpy(request.name, ifp->name, sizeof(ifp->name)); + request.proto = LDP_IGP_SYNC_IF_STATE_REQUEST; + request.ifindex = ifp->ifindex; + + zclient_send_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST, + (uint8_t *)&request, sizeof(request)); +} + +/* + * LDP-SYNC general interface routines + */ +void ospf_ldp_sync_if_init(struct ospf_interface *oi) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + struct interface *ifp = oi->ifp; + + /* called when OSPF is configured on an interface: + * if LDP-IGP Sync is configured globally set state + * if ptop interface inform LDP LDP-SYNC is enabled + */ + if (if_is_loopback(ifp) || (ifp->vrf->vrf_id != VRF_DEFAULT) + || !(CHECK_FLAG(oi->ospf->ldp_sync_cmd.flags, + LDP_SYNC_FLAG_ENABLE))) + return; + + ols_debug("%s: init if %s", __func__, ifp->name); + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + + ldp_sync_info = params->ldp_sync_info; + + /* specified on interface overrides global config. */ + if (!CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN)) + ldp_sync_info->holddown = oi->ospf->ldp_sync_cmd.holddown; + + if (!CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG)) + ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED; + + if ((params->type == OSPF_IFTYPE_POINTOPOINT || + if_is_pointopoint(ifp)) && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; +} + +void ospf_ldp_sync_if_start(struct interface *ifp, bool send_state_req) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + + /* Start LDP-SYNC on this interface: + * set cost of interface to LSInfinity so traffic will use different + * interface until LDP has learned all labels from peer + * start holddown timer if configured + * send msg to LDP to get LDP-SYNC state + */ + if (ldp_sync_info && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED && + ldp_sync_info->state != LDP_IGP_SYNC_STATE_NOT_REQUIRED) { + ols_debug("%s: start on if %s state: %s", __func__, ifp->name, + "Holding down until Sync"); + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + ospf_if_recalculate_output_cost(ifp); + ospf_ldp_sync_holddown_timer_add(ifp); + + if (send_state_req) + ospf_ldp_sync_state_req_msg(ifp); + } +} + +void ospf_ldp_sync_if_complete(struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + + /* received sync-complete from LDP: + * set state to up + * stop timer + * restore interface cost to original value + */ + if (ldp_sync_info && ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) { + if (ldp_sync_info->state == LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP) + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_UP; + EVENT_OFF(ldp_sync_info->t_holddown); + ospf_if_recalculate_output_cost(ifp); + } +} + +void ospf_ldp_sync_handle_client_close(struct zapi_client_close_info *info) +{ + struct ospf *ospf; + struct vrf *vrf; + struct interface *ifp; + + /* if ospf is not enabled or LDP-SYNC is not configured ignore */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL + || !CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + return; + + /* Check if the LDP main client session closed */ + if (info->proto != ZEBRA_ROUTE_LDP || info->session_id == 0) + return; + + /* Handle the zebra notification that the LDP client session closed. + * set cost to LSInfinity + * send request to LDP for LDP-SYNC state for each interface + */ + zlog_err("%s: LDP down", __func__); + + vrf = vrf_lookup_by_id(ospf->vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) + ospf_ldp_sync_ldp_fail(ifp); +} + +void ospf_ldp_sync_ldp_fail(struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + + /* LDP client close detected: + * stop holddown timer + * set cost of interface to LSInfinity so traffic will use different + * interface until LDP has learned all labels from peer + */ + if (ldp_sync_info && + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED && + ldp_sync_info->state != LDP_IGP_SYNC_STATE_NOT_REQUIRED) { + EVENT_OFF(ldp_sync_info->t_holddown); + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + ospf_if_recalculate_output_cost(ifp); + } +} + +void ospf_ldp_sync_if_down(struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + + if (ldp_sync_if_down(ldp_sync_info) == false) + return; + + ols_debug("%s: down on if %s", __func__, ifp->name); + + /* Interface down: + * can occur from a link down or changing config + * ospf network type change interface is brought down/up + */ + switch (ldp_sync_info->state) { + case LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP: + case LDP_IGP_SYNC_STATE_REQUIRED_UP: + if (params->type != OSPF_IFTYPE_POINTOPOINT && + !if_is_pointopoint(ifp)) + /* LDP-SYNC not able to run on non-ptop interface */ + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + break; + case LDP_IGP_SYNC_STATE_NOT_REQUIRED: + if (params->type == OSPF_IFTYPE_POINTOPOINT || + if_is_pointopoint(ifp)) + /* LDP-SYNC is able to run on ptop interface */ + ldp_sync_info->state = + LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + break; + default: + break; + } +} + +void ospf_ldp_sync_if_remove(struct interface *ifp, bool remove) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + return; + + ldp_sync_info = params->ldp_sync_info; + + /* Stop LDP-SYNC on this interface: + * if holddown timer is running stop it + * delete ldp instance on interface + * restore cost + */ + ols_debug("%s: Removed from if %s", __func__, ifp->name); + + EVENT_OFF(ldp_sync_info->t_holddown); + + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + ospf_if_recalculate_output_cost(ifp); + if (!CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG)) + ldp_sync_info->enabled = LDP_IGP_SYNC_DEFAULT; + if (remove) { + ldp_sync_info_free(&ldp_sync_info); + params->ldp_sync_info = NULL; + } +} + +static int ospf_ldp_sync_ism_change(struct ospf_interface *oi, int state, + int old_state) +{ + /* Terminal state or regression */ + switch (state) { + case ISM_PointToPoint: + /* If LDP-SYNC is configure on interface then start */ + ospf_ldp_sync_if_start(oi->ifp, true); + break; + case ISM_Down: + /* If LDP-SYNC is configure on this interface then stop it */ + ospf_ldp_sync_if_down(oi->ifp); + break; + default: + break; + } + return 0; +} + +/* + * LDP-SYNC holddown timer routines + */ +static void ospf_ldp_sync_holddown_timer(struct event *thread) +{ + struct interface *ifp; + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + /* holddown timer expired: + * didn't receive msg from LDP indicating sync-complete + * restore interface cost to original value + */ + ifp = EVENT_ARG(thread); + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info) { + ldp_sync_info = params->ldp_sync_info; + + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_UP; + + ols_debug("%s: holddown timer expired for %s state: %s", + __func__, ifp->name, "Sync achieved"); + + ospf_if_recalculate_output_cost(ifp); + } +} + +void ospf_ldp_sync_holddown_timer_add(struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + + /* Start holddown timer: + * this timer is used to keep interface cost at LSInfinity + * once expires returns cost to original value + * if timer is already running or holddown time is off just return + */ + if (ldp_sync_info->t_holddown || + ldp_sync_info->holddown == LDP_IGP_SYNC_HOLDDOWN_DEFAULT) + return; + + ols_debug("%s: start holddown timer for %s time %d", __func__, + ifp->name, ldp_sync_info->holddown); + + event_add_timer(master, ospf_ldp_sync_holddown_timer, ifp, + ldp_sync_info->holddown, &ldp_sync_info->t_holddown); +} + +/* + * LDP-SYNC exit routes. + */ +void ospf_ldp_sync_gbl_exit(struct ospf *ospf, bool remove) +{ + struct interface *ifp; + struct vrf *vrf; + + /* ospf is being removed + * stop any holddown timers + */ + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + /* unregister with opaque client to recv LDP-IGP Sync msgs */ + zclient_unregister_opaque(zclient, + LDP_IGP_SYNC_IF_STATE_UPDATE); + zclient_unregister_opaque(zclient, + LDP_IGP_SYNC_ANNOUNCE_UPDATE); + + /* disable LDP globally */ + UNSET_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE); + UNSET_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN); + ospf->ldp_sync_cmd.holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + + /* turn off LDP-IGP Sync on all OSPF interfaces */ + vrf = vrf_lookup_by_id(ospf->vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) + ospf_ldp_sync_if_remove(ifp, remove); + } +} + +/* + * LDP-SYNC routes used by set commands. + */ +void ospf_if_set_ldp_sync_enable(struct ospf *ospf, struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + /* called when setting LDP-SYNC at the global level: + * specified on interface overrides global config + * if ptop link send msg to LDP indicating ldp-sync enabled + */ + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + ldp_sync_info = params->ldp_sync_info; + + /* config on interface, overrides global config. */ + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG)) + if (ldp_sync_info->enabled != LDP_IGP_SYNC_ENABLED) + return; + + ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED; + + ols_debug("%s: enable if %s", __func__, ifp->name); + + /* send message to LDP if ptop link */ + if (params->type == OSPF_IFTYPE_POINTOPOINT || + if_is_pointopoint(ifp)) { + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + ospf_ldp_sync_state_req_msg(ifp); + } else { + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + zlog_debug("%s: Sync only runs on P2P links %s", __func__, + ifp->name); + } +} + +void ospf_if_set_ldp_sync_holddown(struct ospf *ospf, struct interface *ifp) +{ + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + /* called when setting LDP-SYNC at the global level: + * specified on interface overrides global config. + */ + if (if_is_loopback(ifp)) + return; + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + ldp_sync_info = params->ldp_sync_info; + + /* config on interface, overrides global config. */ + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN)) + return; + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN)) + ldp_sync_info->holddown = ospf->ldp_sync_cmd.holddown; + else + ldp_sync_info->holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; +} + +/* + * LDP-SYNC routines used by show commands. + */ + +void ospf_ldp_sync_show_info(struct vty *vty, struct ospf *ospf, + json_object *json_vrf, bool use_json) +{ + + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + if (use_json) { + json_object_boolean_true_add(json_vrf, + "mplsLdpIgpSyncEnabled"); + json_object_int_add(json_vrf, "mplsLdpIgpSyncHolddown", + ospf->ldp_sync_cmd.holddown); + } else { + vty_out(vty, " MPLS LDP-IGP Sync is enabled\n"); + if (ospf->ldp_sync_cmd.holddown == 0) + vty_out(vty, + " MPLS LDP-IGP Sync holddown timer is disabled\n"); + else + vty_out(vty, + " MPLS LDP-IGP Sync holddown timer %d sec\n", + ospf->ldp_sync_cmd.holddown); + } + } +} + +static void show_ip_ospf_mpls_ldp_interface_sub(struct vty *vty, + struct ospf_interface *oi, + struct interface *ifp, + json_object *json_interface_sub, + bool use_json) +{ + const char *ldp_state; + struct ospf_if_params *params; + char timebuf[OSPF_TIME_DUMP_SIZE]; + struct ldp_sync_info *ldp_sync_info; + + params = IF_DEF_PARAMS(oi->ifp); + if (params->ldp_sync_info == NULL) + return; + + ldp_sync_info = params->ldp_sync_info; + if (use_json) { + if (ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) + json_object_boolean_true_add(json_interface_sub, + "ldpIgpSyncEnabled"); + else + json_object_boolean_false_add(json_interface_sub, + "ldpIgpSyncEnabled"); + + json_object_int_add(json_interface_sub, "holdDownTimeInSec", + ldp_sync_info->holddown); + + } else { + vty_out(vty, "%-10s\n", ifp->name); + vty_out(vty, " LDP-IGP Synchronization enabled: %s\n", + ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED + ? "yes" + : "no"); + vty_out(vty, " Holddown timer in seconds: %u\n", + ldp_sync_info->holddown); + } + + switch (ldp_sync_info->state) { + case LDP_IGP_SYNC_STATE_REQUIRED_UP: + if (use_json) + json_object_string_add(json_interface_sub, + "ldpIgpSyncState", + "Sync achieved"); + else + vty_out(vty, " State: Sync achieved\n"); + break; + case LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP: + if (ldp_sync_info->t_holddown != NULL) { + if (use_json) { + long time_store; + + time_store = monotime_until( + &ldp_sync_info->t_holddown->u.sands, + NULL) + /1000LL; + + json_object_int_add(json_interface_sub, + "ldpIgpSyncTimeRemainInMsec", + time_store); + + json_object_string_add(json_interface_sub, + "ldpIgpSyncState", + "Holding down until Sync"); + } else { + vty_out(vty, + " Holddown timer is running %s remaining\n", + ospf_timer_dump( + ldp_sync_info->t_holddown, + timebuf, + sizeof(timebuf))); + + vty_out(vty, + " State: Holding down until Sync\n"); + } + } else { + if (use_json) + json_object_string_add(json_interface_sub, + "ldpIgpSyncState", + "Sync not achieved"); + else + vty_out(vty, " State: Sync not achieved\n"); + } + break; + case LDP_IGP_SYNC_STATE_NOT_REQUIRED: + default: + if (IF_DEF_PARAMS(ifp)->type != OSPF_IFTYPE_POINTOPOINT && + !if_is_pointopoint(ifp)) + ldp_state = "Sync not required: non-p2p link"; + else + ldp_state = "Sync not required"; + + if (use_json) + json_object_string_add(json_interface_sub, + "ldpIgpSyncState", + ldp_state); + else + vty_out(vty, " State: %s\n", ldp_state); + break; + } +} + +static int show_ip_ospf_mpls_ldp_interface_common(struct vty *vty, + struct ospf *ospf, + char *intf_name, + json_object *json, + bool use_json) +{ + struct interface *ifp; + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + json_object *json_interface_sub = NULL; + + if (intf_name == NULL) { + /* Show All Interfaces.*/ + FOR_ALL_INTERFACES (vrf, ifp) { + struct route_node *rn; + struct ospf_interface *oi; + + if (ospf_oi_count(ifp) == 0 && !use_json) { + if (!if_is_up(ifp)) + vty_out(vty, "%s\n Interface down\n", + ifp->name); + continue; + } + for (rn = route_top(IF_OIFS(ifp)); rn; + rn = route_next(rn)) { + oi = rn->info; + + if (oi == NULL) + continue; + + if (use_json) { + json_interface_sub = + json_object_new_object(); + } + show_ip_ospf_mpls_ldp_interface_sub( + vty, oi, ifp, json_interface_sub, + use_json); + + if (use_json) { + json_object_object_add( + json, ifp->name, + json_interface_sub); + } + } + } + } else { + /* Interface name is specified. */ + ifp = if_lookup_by_name(intf_name, ospf->vrf_id); + if (ifp != NULL) { + struct route_node *rn; + struct ospf_interface *oi; + + if (ospf_oi_count(ifp) == 0 && !use_json) { + if (if_is_up(ifp)) + vty_out(vty, "%s\n OSPF not enabled\n", + ifp->name); + else + vty_out(vty, "%s\n Interface down\n", + ifp->name); + return CMD_SUCCESS; + } + for (rn = route_top(IF_OIFS(ifp)); rn; + rn = route_next(rn)) { + oi = rn->info; + + if (oi == NULL) + continue; + + if (use_json) + json_interface_sub = + json_object_new_object(); + + show_ip_ospf_mpls_ldp_interface_sub( + vty, oi, ifp, json_interface_sub, + use_json); + + if (use_json) { + json_object_object_add( + json, ifp->name, + json_interface_sub); + } + } + } + } + return CMD_SUCCESS; +} + +/* + * Write the global LDP-SYNC configuration. + */ +void ospf_ldp_sync_write_config(struct vty *vty, struct ospf *ospf) +{ + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) + vty_out(vty, " mpls ldp-sync\n"); + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN)) + vty_out(vty, " mpls ldp-sync holddown %u\n", + ospf->ldp_sync_cmd.holddown); +} + +/* + * Write the interface LDP-SYNC configuration. + */ +void ospf_ldp_sync_if_write_config(struct vty *vty, + struct ospf_if_params *params) + +{ + struct ldp_sync_info *ldp_sync_info; + + ldp_sync_info = params->ldp_sync_info; + if (ldp_sync_info == NULL) + return; + + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG)) { + if (ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) + vty_out(vty, " ip ospf mpls ldp-sync\n"); + else + vty_out(vty, " no ip ospf mpls ldp-sync\n"); + } + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN)) + vty_out(vty, " ip ospf mpls ldp-sync holddown %u\n", + ldp_sync_info->holddown); +} + +/* + * LDP-SYNC commands. + */ +#include "ospfd/ospf_ldp_sync_clippy.c" + +DEFPY (ospf_mpls_ldp_sync, + ospf_mpls_ldp_sync_cmd, + "mpls ldp-sync", + "MPLS specific commands\n" + "Enable MPLS LDP-IGP Sync\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + struct interface *ifp; + + if (ospf->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + /* register with opaque client to recv LDP-IGP Sync msgs */ + zclient_register_opaque(zclient, LDP_IGP_SYNC_IF_STATE_UPDATE); + zclient_register_opaque(zclient, LDP_IGP_SYNC_ANNOUNCE_UPDATE); + + if (!CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + SET_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE); + /* turn on LDP-IGP Sync on all ptop OSPF interfaces */ + FOR_ALL_INTERFACES (vrf, ifp) + ospf_if_set_ldp_sync_enable(ospf, ifp); + } + return CMD_SUCCESS; +} + +DEFPY (no_ospf_mpls_ldp_sync, + no_ospf_mpls_ldp_sync_cmd, + "no mpls ldp-sync", + NO_STR + "MPLS specific commands\n" + "Disable MPLS LDP-IGP Sync\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + ospf_ldp_sync_gbl_exit(ospf, false); + return CMD_SUCCESS; +} + +DEFPY (ospf_mpls_ldp_sync_holddown, + ospf_mpls_ldp_sync_holddown_cmd, + "mpls ldp-sync holddown (1-10000)", + "MPLS specific commands\n" + "Enable MPLS LDP-IGP Sync\n" + "Set holddown timer\n" + "seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + struct interface *ifp; + + if (ospf->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + SET_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN); + ospf->ldp_sync_cmd.holddown = holddown; + /* set holddown time on all OSPF interfaces */ + FOR_ALL_INTERFACES (vrf, ifp) + ospf_if_set_ldp_sync_holddown(ospf, ifp); + + return CMD_SUCCESS; +} + +DEFPY (no_ospf_mpls_ldp_sync_holddown, + no_ospf_mpls_ldp_sync_holddown_cmd, + "no mpls ldp-sync holddown [<(1-10000)>]", + NO_STR + "MPLS specific commands\n" + "Disable MPLS LDP-IGP Sync\n" + "holddown timer disable\n" + "Time in seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + struct interface *ifp; + + if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN)) { + UNSET_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_HOLDDOWN); + ospf->ldp_sync_cmd.holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + /* turn off holddown timer on all OSPF interfaces */ + FOR_ALL_INTERFACES (vrf, ifp) + ospf_if_set_ldp_sync_holddown(ospf, ifp); + } + return CMD_SUCCESS; +} + + +DEFPY (mpls_ldp_sync, + mpls_ldp_sync_cmd, + "ip ospf mpls ldp-sync", + IP_STR + "OSPF interface commands\n" + MPLS_STR + MPLS_LDP_SYNC_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync does not run on loopback interface\n"); + return CMD_ERR_NOTHING_TODO; + } + + if (ifp->vrf->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + + ldp_sync_info = params->ldp_sync_info; + + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG); + ldp_sync_info->enabled = LDP_IGP_SYNC_ENABLED; + if (params->type == OSPF_IFTYPE_POINTOPOINT || if_is_pointopoint(ifp)) { + ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; + ospf_ldp_sync_state_req_msg(ifp); + } else { + zlog_debug("ldp_sync: only runs on P2P links %s", ifp->name); + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + } + return CMD_SUCCESS; +} + +DEFPY (no_mpls_ldp_sync, + no_mpls_ldp_sync_cmd, + "no ip ospf mpls ldp-sync", + NO_STR + IP_STR + "OSPF interface commands\n" + MPLS_STR + NO_MPLS_LDP_SYNC_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync: does not run on loopback interface\n"); + return CMD_ERR_NOTHING_TODO; + } + + if (ifp->vrf->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + + ldp_sync_info = params->ldp_sync_info; + + /* disable LDP-SYNC on an interface + * stop holddown timer if running + * restore ospf cost + */ + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG); + ldp_sync_info->enabled = LDP_IGP_SYNC_DEFAULT; + ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; + EVENT_OFF(ldp_sync_info->t_holddown); + ospf_if_recalculate_output_cost(ifp); + + return CMD_SUCCESS; +} + +DEFPY (mpls_ldp_sync_holddown, + mpls_ldp_sync_holddown_cmd, + "ip ospf mpls ldp-sync holddown (0-10000)", + IP_STR + "OSPF interface commands\n" + MPLS_STR + MPLS_LDP_SYNC_STR + "Time to wait for LDP-SYNC to occur before restoring interface cost\n" + "Time in seconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync: does not run on loopback interface\n"); + return CMD_ERR_NOTHING_TODO; + } + + if (ifp->vrf->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + params = IF_DEF_PARAMS(ifp); + if (params->ldp_sync_info == NULL) + params->ldp_sync_info = ldp_sync_info_create(); + + ldp_sync_info = params->ldp_sync_info; + + SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN); + ldp_sync_info->holddown = holddown; + + return CMD_SUCCESS; +} + +DEFPY (no_mpls_ldp_sync_holddown, + no_mpls_ldp_sync_holddown_cmd, + "no ip ospf mpls ldp-sync holddown [<(1-10000)>]", + NO_STR + IP_STR + "OSPF interface commands\n" + MPLS_STR + NO_MPLS_LDP_SYNC_STR + NO_MPLS_LDP_SYNC_HOLDDOWN_STR + "Time in seconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + struct ldp_sync_info *ldp_sync_info; + struct ospf *ospf; + + if (if_is_loopback(ifp)) { + vty_out(vty, "ldp-sync: does not run on loopback interface\n"); + return CMD_ERR_NOTHING_TODO; + } + + if (ifp->vrf->vrf_id != VRF_DEFAULT) { + vty_out(vty, "ldp-sync only runs on DEFAULT VRF\n"); + return CMD_ERR_NOTHING_TODO; + } + + params = IF_DEF_PARAMS(ifp); + ldp_sync_info = params->ldp_sync_info; + if (ldp_sync_info == NULL) + return CMD_SUCCESS; + + /* use global configured value if set */ + if (CHECK_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN)) { + UNSET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_HOLDDOWN); + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf && CHECK_FLAG(ospf->ldp_sync_cmd.flags, + LDP_SYNC_FLAG_HOLDDOWN)) + ldp_sync_info->holddown = ospf->ldp_sync_cmd.holddown; + else + ldp_sync_info->holddown = LDP_IGP_SYNC_HOLDDOWN_DEFAULT; + } + return CMD_SUCCESS; +} + +DEFPY (show_ip_ospf_mpls_ldp_interface, + show_ip_ospf_mpls_ldp_interface_cmd, + "show ip ospf mpls ldp-sync [interface ] [json]", + SHOW_STR + IP_STR + "OSPF information\n" + MPLS_STR + "LDP-IGP Sync information\n" + "Interface information\n" + "Interface name\n" + "All interfaces\n" + JSON_STR) +{ + struct ospf *ospf; + bool uj = use_json(argc, argv); + char *intf_name = NULL; + int ret = CMD_SUCCESS; + int idx_intf = 0; + json_object *json = NULL; + + if (argv_find(argv, argc, "INTERFACE", &idx_intf)) + intf_name = argv[idx_intf]->arg; + + if (uj) + json = json_object_new_object(); + + /* Display default ospf (instance 0) info */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + vty_json(vty, json); + else + vty_out(vty, "%% OSPF instance not found\n"); + return CMD_SUCCESS; + } + + if (!CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) { + if (uj) + vty_json(vty, json); + else + vty_out(vty, "LDP-sync is disabled\n"); + return CMD_SUCCESS; + } + + ret = show_ip_ospf_mpls_ldp_interface_common(vty, ospf, intf_name, + json, uj); + if (uj) + vty_json(vty, json); + + return ret; +} + +void ospf_ldp_sync_init(void) +{ + /* Install global ldp-igp sync commands */ + install_element(OSPF_NODE, &ospf_mpls_ldp_sync_cmd); + install_element(OSPF_NODE, &no_ospf_mpls_ldp_sync_cmd); + install_element(OSPF_NODE, &ospf_mpls_ldp_sync_holddown_cmd); + install_element(OSPF_NODE, &no_ospf_mpls_ldp_sync_holddown_cmd); + + /* Interface lsp-igp sync commands */ + install_element(INTERFACE_NODE, &mpls_ldp_sync_cmd); + install_element(INTERFACE_NODE, &no_mpls_ldp_sync_cmd); + install_element(INTERFACE_NODE, &mpls_ldp_sync_holddown_cmd); + install_element(INTERFACE_NODE, &no_mpls_ldp_sync_holddown_cmd); + + /* "show ip ospf mpls ldp interface" commands. */ + install_element(VIEW_NODE, &show_ip_ospf_mpls_ldp_interface_cmd); + + hook_register(ospf_ism_change, ospf_ldp_sync_ism_change); + +} diff --git a/ospfd/ospf_ldp_sync.h b/ospfd/ospf_ldp_sync.h new file mode 100644 index 0000000..88fdcba --- /dev/null +++ b/ospfd/ospf_ldp_sync.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ospf_ldp_sync.h: OSPF LDP-IGP Sync handling routines + * Copyright (C) 2020 Volta Networks, Inc. + */ + +#ifndef _ZEBRA_OSPF_LDP_SYNC_H +#define _ZEBRA_OSPF_LDP_SYNC_H + +#define LDP_OSPF_LSINFINITY 65535 + +/* Macro to log debug message */ +#define ols_debug(...) \ + do { \ + if (IS_DEBUG_OSPF_LDP_SYNC) \ + zlog_debug(__VA_ARGS__); \ + } while (0) + + +extern void ospf_if_set_ldp_sync_enable(struct ospf *ospf, + struct interface *ifp); +extern void ospf_if_set_ldp_sync_holddown(struct ospf *ospf, + struct interface *ifp); +extern void ospf_ldp_sync_if_init(struct ospf_interface *ospf); +extern void ospf_ldp_sync_if_start(struct interface *ifp, bool send_state_req); +extern void ospf_ldp_sync_if_remove(struct interface *ifp, bool remove); +extern void ospf_ldp_sync_if_down(struct interface *ifp); +extern void ospf_ldp_sync_if_complete(struct interface *ifp); +extern void ospf_ldp_sync_holddown_timer_add(struct interface *ifp); +extern void ospf_ldp_sync_ldp_fail(struct interface *ifp); +extern void ospf_ldp_sync_show_info(struct vty *vty, struct ospf *ospf, + json_object *json_vrf, bool use_json); +extern void ospf_ldp_sync_write_config(struct vty *vty, struct ospf *ospf); +extern void ospf_ldp_sync_if_write_config(struct vty *vty, + struct ospf_if_params *params); +extern int ospf_ldp_sync_state_update(struct ldp_igp_sync_if_state state); +extern int ospf_ldp_sync_announce_update(struct ldp_igp_sync_announce announce); +extern void +ospf_ldp_sync_handle_client_close(struct zapi_client_close_info *info); +extern void ospf_ldp_sync_state_req_msg(struct interface *ifp); +extern void ospf_ldp_sync_init(void); +extern void ospf_ldp_sync_gbl_exit(struct ospf *ospf, bool remove); +#endif /* _ZEBRA_OSPF_LDP_SYNC_H */ diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c new file mode 100644 index 0000000..e47f832 --- /dev/null +++ b/ospfd/ospf_lsa.c @@ -0,0 +1,4271 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF Link State Advertisement + * Copyright (C) 1999, 2000 Toshiaki Takada + */ + +#include + +#include "monotime.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "memory.h" +#include "stream.h" +#include "log.h" +#include "frrevent.h" +#include "hash.h" +#include "sockunion.h" /* for inet_aton() */ +#include "checksum.h" +#include "network.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_errors.h" + +static struct ospf_lsa *ospf_handle_summarylsa_lsId_chg(struct ospf_area *area, + struct prefix_ipv4 *p, + uint8_t type, + uint32_t metric, + struct in_addr old_id); +static struct ospf_lsa * +ospf_summary_lsa_prepare_and_flood(struct prefix_ipv4 *p, uint32_t metric, + struct ospf_area *area, struct in_addr id); +static struct ospf_lsa *ospf_summary_lsa_refresh(struct ospf *ospf, + struct ospf_lsa *lsa); +static struct ospf_lsa * +ospf_asbr_summary_lsa_prepare_and_flood(struct prefix_ipv4 *p, uint32_t metric, + struct ospf_area *area, + struct in_addr id); +static struct ospf_lsa *ospf_summary_asbr_lsa_refresh(struct ospf *ospf, + struct ospf_lsa *lsa); +static struct ospf_lsa *ospf_handle_exnl_lsa_lsId_chg(struct ospf *ospf, + struct external_info *ei, + struct in_addr id); +static struct ospf_lsa * +ospf_exnl_lsa_prepare_and_flood(struct ospf *ospf, struct external_info *ei, + struct in_addr id); + +uint32_t get_metric(uint8_t *metric) +{ + uint32_t m; + m = metric[0]; + m = (m << 8) + metric[1]; + m = (m << 8) + metric[2]; + return m; +} + +/** @brief The Function checks self generated DoNotAge. + * @param lsa pointer. + * @return true or false. + */ +bool ospf_check_dna_lsa(const struct ospf_lsa *lsa) +{ + return ((IS_LSA_SELF(lsa) && CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE)) + ? true + : false); +} + +struct timeval int2tv(int a) +{ + struct timeval ret; + + ret.tv_sec = a; + ret.tv_usec = 0; + + return ret; +} + +struct timeval msec2tv(int a) +{ + struct timeval ret; + + ret.tv_sec = a / 1000; + ret.tv_usec = (a % 1000) * 1000; + + return ret; +} + +int ospf_lsa_refresh_delay(struct ospf_lsa *lsa) +{ + struct timeval delta; + int delay = 0; + + if (monotime_since(&lsa->tv_orig, &delta) + < OSPF_MIN_LS_INTERVAL * 1000LL) { + struct timeval minv = msec2tv(OSPF_MIN_LS_INTERVAL); + timersub(&minv, &delta, &minv); + + /* TBD: remove padding to full sec, return timeval instead */ + delay = minv.tv_sec + !!minv.tv_usec; + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type%d:%pI4]: Refresh timer delay %d seconds", + lsa->data->type, &lsa->data->id, + delay); + + assert(delay > 0); + } + + return delay; +} + + +int get_age(struct ospf_lsa *lsa) +{ + struct timeval rel; + + /* As per rfc4136, the self-originated LSAs in their + * own database keep aging, however rfc doesn't tell + * till how long the LSA should be aged, as of now + * we are capping it for OSPF_LSA_MAXAGE. + */ + + /* If LSA is marked as donotage */ + if (CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE) && !IS_LSA_SELF(lsa)) + return ntohs(lsa->data->ls_age); + + monotime_since(&lsa->tv_recv, &rel); + return ntohs(lsa->data->ls_age) + rel.tv_sec; +} + + +/* Fletcher Checksum -- Refer to RFC1008. */ + +/* All the offsets are zero-based. The offsets in the RFC1008 are + one-based. */ +uint16_t ospf_lsa_checksum(struct lsa_header *lsa) +{ + uint8_t *buffer = &lsa->options; + int options_offset = buffer - (uint8_t *)&lsa->ls_age; /* should be 2 */ + + /* Skip the AGE field */ + uint16_t len = ntohs(lsa->length) - options_offset; + + /* Checksum offset starts from "options" field, not the beginning of the + lsa_header struct. The offset is 14, rather than 16. */ + int checksum_offset = (uint8_t *)&lsa->checksum - buffer; + + return fletcher_checksum(buffer, len, checksum_offset); +} + +int ospf_lsa_checksum_valid(struct lsa_header *lsa) +{ + uint8_t *buffer = &lsa->options; + int options_offset = buffer - (uint8_t *)&lsa->ls_age; /* should be 2 */ + + /* Skip the AGE field */ + uint16_t len = ntohs(lsa->length) - options_offset; + + return (fletcher_checksum(buffer, len, FLETCHER_CHECKSUM_VALIDATE) + == 0); +} + + +/* Create OSPF LSA. */ +struct ospf_lsa *ospf_lsa_new(void) +{ + struct ospf_lsa *new; + + new = XCALLOC(MTYPE_OSPF_LSA, sizeof(struct ospf_lsa)); + + new->flags = 0; + new->lock = 1; + new->retransmit_counter = 0; + monotime(&new->tv_recv); + new->tv_orig = new->tv_recv; + new->refresh_list = -1; + new->vrf_id = VRF_DEFAULT; + new->to_be_acknowledged = 0; + new->opaque_zero_len_delete = 0; + + return new; +} + +struct ospf_lsa *ospf_lsa_new_and_data(size_t size) +{ + struct ospf_lsa *new; + + new = ospf_lsa_new(); + new->data = ospf_lsa_data_new(size); + new->size = size; + + return new; +} + +/* Duplicate OSPF LSA. */ +struct ospf_lsa *ospf_lsa_dup(struct ospf_lsa *lsa) +{ + struct ospf_lsa *new; + + if (lsa == NULL) + return NULL; + + new = XCALLOC(MTYPE_OSPF_LSA, sizeof(struct ospf_lsa)); + + memcpy(new, lsa, sizeof(struct ospf_lsa)); + UNSET_FLAG(new->flags, OSPF_LSA_DISCARD); + new->lock = 1; + new->retransmit_counter = 0; + new->data = ospf_lsa_data_dup(lsa->data); + + /* kevinm: Clear the refresh_list, otherwise there are going + to be problems when we try to remove the LSA from the + queue (which it's not a member of.) + XXX: Should we add the LSA to the refresh_list queue? */ + new->refresh_list = -1; + + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug("LSA: duplicated %p (new: %p)", (void *)lsa, + (void *)new); + + return new; +} + +/* Free OSPF LSA. */ +void ospf_lsa_free(struct ospf_lsa *lsa) +{ + assert(lsa->lock == 0); + + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug("LSA: freed %p", (void *)lsa); + + /* Delete LSA data. */ + if (lsa->data != NULL) + ospf_lsa_data_free(lsa->data); + + assert(lsa->refresh_list < 0); + + memset(lsa, 0, sizeof(struct ospf_lsa)); + XFREE(MTYPE_OSPF_LSA, lsa); +} + +/* Lock LSA. */ +struct ospf_lsa *ospf_lsa_lock(struct ospf_lsa *lsa) +{ + lsa->lock++; + return lsa; +} + +/* Unlock LSA. */ +void ospf_lsa_unlock(struct ospf_lsa **lsa) +{ + /* This is sanity check. */ + if (!lsa || !*lsa) + return; + + (*lsa)->lock--; + + assert((*lsa)->lock >= 0); + + if ((*lsa)->lock == 0) { + assert(CHECK_FLAG((*lsa)->flags, OSPF_LSA_DISCARD)); + ospf_lsa_free(*lsa); + *lsa = NULL; + } +} + +/* Check discard flag. */ +void ospf_lsa_discard(struct ospf_lsa *lsa) +{ + if (!CHECK_FLAG(lsa->flags, OSPF_LSA_DISCARD)) { + SET_FLAG(lsa->flags, OSPF_LSA_DISCARD); + ospf_lsa_unlock(&lsa); + } +} + +/* Create LSA data. */ +struct lsa_header *ospf_lsa_data_new(size_t size) +{ + return XCALLOC(MTYPE_OSPF_LSA_DATA, size); +} + +/* Duplicate LSA data. */ +struct lsa_header *ospf_lsa_data_dup(struct lsa_header *lsah) +{ + struct lsa_header *new; + + new = ospf_lsa_data_new(ntohs(lsah->length)); + memcpy(new, lsah, ntohs(lsah->length)); + + return new; +} + +/* Free LSA data. */ +void ospf_lsa_data_free(struct lsa_header *lsah) +{ + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug("LSA[Type%d:%pI4]: data freed %p", lsah->type, + &lsah->id, (void *)lsah); + + XFREE(MTYPE_OSPF_LSA_DATA, lsah); +} + + +/* LSA general functions. */ + +const char *dump_lsa_key(struct ospf_lsa *lsa) +{ + static char buf[sizeof("Type255,id(255.255.255.255),ar(255.255.255.255)")+1]; + struct lsa_header *lsah; + + if (lsa != NULL && (lsah = lsa->data) != NULL) { + char id[INET_ADDRSTRLEN], ar[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &lsah->id, id, sizeof(id)); + inet_ntop(AF_INET, &lsah->adv_router, ar, sizeof(ar)); + + snprintf(buf, sizeof(buf), "Type%d,id(%s),ar(%s)", lsah->type, + id, ar); + } else + strlcpy(buf, "NULL", sizeof(buf)); + + return buf; +} + +uint32_t lsa_seqnum_increment(struct ospf_lsa *lsa) +{ + uint32_t seqnum; + + seqnum = ntohl(lsa->data->ls_seqnum) + 1; + + return htonl(seqnum); +} + +void lsa_header_set(struct stream *s, uint8_t options, uint8_t type, + struct in_addr id, struct in_addr router_id) +{ + struct lsa_header *lsah; + + lsah = (struct lsa_header *)STREAM_DATA(s); + + lsah->ls_age = htons(OSPF_LSA_INITIAL_AGE); + lsah->options = options; + lsah->type = type; + lsah->id = id; + lsah->adv_router = router_id; + lsah->ls_seqnum = htonl(OSPF_INITIAL_SEQUENCE_NUMBER); + + stream_forward_endp(s, OSPF_LSA_HEADER_SIZE); +} + + +/* router-LSA related functions. */ +/* Get router-LSA flags. */ +uint8_t router_lsa_flags(struct ospf_area *area) +{ + uint8_t flags; + + flags = area->ospf->flags; + + /* Set virtual link flag. */ + if (ospf_full_virtual_nbrs(area)) + SET_FLAG(flags, ROUTER_LSA_VIRTUAL); + else + /* Just sanity check */ + UNSET_FLAG(flags, ROUTER_LSA_VIRTUAL); + + /* Set Shortcut ABR behabiour flag. */ + UNSET_FLAG(flags, ROUTER_LSA_SHORTCUT); + if (area->ospf->abr_type == OSPF_ABR_SHORTCUT) + if (!OSPF_IS_AREA_BACKBONE(area)) + if ((area->shortcut_configured == OSPF_SHORTCUT_DEFAULT + && area->ospf->backbone == NULL) + || area->shortcut_configured + == OSPF_SHORTCUT_ENABLE) + SET_FLAG(flags, ROUTER_LSA_SHORTCUT); + + /* ASBR can't exit in stub area. */ + if (area->external_routing == OSPF_AREA_STUB) + UNSET_FLAG(flags, ROUTER_LSA_EXTERNAL); + /* If ASBR set External flag */ + else if (IS_OSPF_ASBR(area->ospf)) + SET_FLAG(flags, ROUTER_LSA_EXTERNAL); + + /* Set ABR dependent flags */ + if (IS_OSPF_ABR(area->ospf)) { + SET_FLAG(flags, ROUTER_LSA_BORDER); + /* If Area is NSSA and we are both ABR and unconditional + * translator, + * set Nt bit to inform other routers. + */ + if ((area->external_routing == OSPF_AREA_NSSA) + && (area->NSSATranslatorRole == OSPF_NSSA_ROLE_ALWAYS)) + SET_FLAG(flags, ROUTER_LSA_NT); + } + return flags; +} + +/* Lookup neighbor other than myself. + And check neighbor count, + Point-to-Point link must have only 1 neighbor. */ +struct ospf_neighbor *ospf_nbr_lookup_ptop(struct ospf_interface *oi) +{ + struct ospf_neighbor *nbr = NULL; + struct route_node *rn; + + /* Search neighbor, there must be one of two nbrs. */ + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) + if ((nbr = rn->info)) + if (!IPV4_ADDR_SAME(&nbr->router_id, + &oi->ospf->router_id)) + if (nbr->state == NSM_Full) { + route_unlock_node(rn); + break; + } + + /* PtoP link must have only 1 neighbor. */ + if (ospf_nbr_count(oi, 0) > 1) + flog_warn( + EC_OSPF_PTP_NEIGHBOR, + "Point-to-Point link on interface %s has more than 1 neighbor.", + oi->ifp->name); + + return nbr; +} + +/* Determine cost of link, taking RFC3137 stub-router support into + * consideration + */ +static uint16_t ospf_link_cost(struct ospf_interface *oi) +{ + /* RFC3137 stub router support */ + if (!CHECK_FLAG(oi->area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED)) + return oi->output_cost; + else + return OSPF_OUTPUT_COST_INFINITE; +} + +/* Set a link information. */ +char link_info_set(struct stream **s, struct in_addr id, struct in_addr data, + uint8_t type, uint8_t tos, uint16_t cost) +{ + /* LSA stream is initially allocated to OSPF_MAX_LSA_SIZE, suits + * vast majority of cases. Some rare routers with lots of links need + * more. + * we try accommodate those here. + */ + if (STREAM_WRITEABLE(*s) < OSPF_ROUTER_LSA_LINK_SIZE) { + size_t ret = OSPF_MAX_LSA_SIZE; + + /* Can we enlarge the stream still? */ + if (STREAM_SIZE(*s) == OSPF_MAX_LSA_SIZE) { + /* we futz the size here for simplicity, really we need + * to account + * for just: + * IP Header - (sizeof(struct ip)) + * OSPF Header - OSPF_HEADER_SIZE + * LSA Header - OSPF_LSA_HEADER_SIZE + * MD5 auth data, if MD5 is configured - + * OSPF_AUTH_MD5_SIZE. + * + * Simpler just to subtract OSPF_MAX_LSA_SIZE though. + */ + ret = stream_resize_inplace( + s, OSPF_MAX_PACKET_SIZE - OSPF_MAX_LSA_SIZE); + } + + if (ret == OSPF_MAX_LSA_SIZE) { + flog_warn( + EC_OSPF_LSA_SIZE, + "%s: Out of space in LSA stream, left %zd, size %zd", + __func__, STREAM_WRITEABLE(*s), + STREAM_SIZE(*s)); + return 0; + } + } + + /* TOS based routing is not supported. */ + stream_put_ipv4(*s, id.s_addr); /* Link ID. */ + stream_put_ipv4(*s, data.s_addr); /* Link Data. */ + stream_putc(*s, type); /* Link Type. */ + stream_putc(*s, tos); /* TOS = 0. */ + stream_putw(*s, cost); /* Link Cost. */ + + return 1; +} + +/* Describe Point-to-Point link (Section 12.4.1.1). */ + +/* Note: If the interface is configured as point-to-point dmvpn then the other + * end of link is dmvpn hub with point-to-multipoint ospf network type. The + * hub then expects this router to populate the stub network and also Link Data + * Field set to IP Address and not MIB-II ifIndex + */ +static int lsa_link_ptop_set(struct stream **s, struct ospf_interface *oi) +{ + int links = 0; + struct ospf_neighbor *nbr; + struct in_addr id, mask, data; + uint16_t cost = ospf_link_cost(oi); + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type1]: Set link Point-to-Point"); + + if ((nbr = ospf_nbr_lookup_ptop(oi))) + if (nbr->state == NSM_Full) { + if (CHECK_FLAG(oi->connected->flags, + ZEBRA_IFA_UNNUMBERED) + && !oi->ptp_dmvpn) { + /* For unnumbered point-to-point networks, the + Link Data field + should specify the interface's MIB-II ifIndex + value. */ + data.s_addr = htonl(oi->ifp->ifindex); + links += link_info_set( + s, nbr->router_id, data, + LSA_LINK_TYPE_POINTOPOINT, 0, cost); + } else { + links += link_info_set( + s, nbr->router_id, + oi->address->u.prefix4, + LSA_LINK_TYPE_POINTOPOINT, 0, cost); + } + } + + /* no need for a stub link for unnumbered interfaces */ + if (OSPF_IF_PARAM(oi, prefix_suppression)) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type1]: Interface %s stub link omitted due prefix-suppression", + oi->ifp->name); + } else { + if (oi->ptp_dmvpn || + !CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) { + /* Regardless of the state of the neighboring router, we must + add a Type 3 link (stub network). + N.B. Options 1 & 2 share basically the same logic. */ + masklen2ip(oi->address->prefixlen, &mask); + id.s_addr = + CONNECTED_PREFIX(oi->connected)->u.prefix4.s_addr & + mask.s_addr; + links += link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, + 0, oi->output_cost); + } + } + + return links; +} + +/* Describe Broadcast Link. */ +static int lsa_link_broadcast_set(struct stream **s, struct ospf_interface *oi) +{ + struct ospf_neighbor *dr; + struct in_addr id, mask; + uint16_t cost = ospf_link_cost(oi); + + /* Describe Type 3 Link. */ + if (oi->state == ISM_Waiting) { + if (OSPF_IF_PARAM(oi, prefix_suppression)) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type1]: Interface %s stub link omitted due prefix-suppression", + oi->ifp->name); + return 0; + } + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type1]: Interface %s is in state Waiting. Adding stub interface", + oi->ifp->name); + masklen2ip(oi->address->prefixlen, &mask); + id.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr; + return link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, 0, + oi->output_cost); + } + + dr = ospf_nbr_lookup_by_addr(oi->nbrs, &DR(oi)); + /* Describe Type 2 link. */ + if (dr && (dr->state == NSM_Full + || IPV4_ADDR_SAME(&oi->address->u.prefix4, &DR(oi))) + && ospf_nbr_count(oi, NSM_Full) > 0) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type1]: Interface %s has a DR. Adding transit interface", + oi->ifp->name); + return link_info_set(s, DR(oi), oi->address->u.prefix4, + LSA_LINK_TYPE_TRANSIT, 0, cost); + } + /* Describe type 3 link. */ + else { + if (OSPF_IF_PARAM(oi, prefix_suppression)) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type1]: Interface %s stub link omitted due prefix-suppression", + oi->ifp->name); + return 0; + } + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type1]: Interface %s has no DR. Adding stub interface", + oi->ifp->name); + masklen2ip(oi->address->prefixlen, &mask); + id.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr; + return link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, 0, + oi->output_cost); + } +} + +static int lsa_link_loopback_set(struct stream **s, struct ospf_interface *oi) +{ + struct in_addr id, mask; + + /* Describe Type 3 Link. */ + if ((oi->state != ISM_Loopback) || OSPF_IF_PARAM(oi, prefix_suppression)) + return 0; + + mask.s_addr = 0xffffffff; + id.s_addr = oi->address->u.prefix4.s_addr; + return link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, 0, + oi->output_cost); +} + +/* Describe Virtual Link. */ +static int lsa_link_virtuallink_set(struct stream **s, + struct ospf_interface *oi) +{ + struct ospf_neighbor *nbr; + uint16_t cost = ospf_link_cost(oi); + + if (oi->state == ISM_PointToPoint) + if ((nbr = ospf_nbr_lookup_ptop(oi))) + if (nbr->state == NSM_Full) { + return link_info_set(s, nbr->router_id, + oi->address->u.prefix4, + LSA_LINK_TYPE_VIRTUALLINK, + 0, cost); + } + + return 0; +} + +#define lsa_link_nbma_set(S,O) lsa_link_broadcast_set (S, O) + +/* this function add for support point-to-multipoint ,see rfc2328 +12.4.1.4.*/ +/* from "edward rrr" + http://marc.theaimsgroup.com/?l=zebra&m=100739222210507&w=2 */ +static int lsa_link_ptomp_set(struct stream **s, struct ospf_interface *oi) +{ + int links = 0; + struct route_node *rn; + struct ospf_neighbor *nbr = NULL; + struct in_addr id, mask; + uint16_t cost = ospf_link_cost(oi); + + if (OSPF_IF_PARAM(oi, prefix_suppression)) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type1]: Interface %s stub link omitted due prefix-suppression", + oi->ifp->name); + } else { + mask.s_addr = 0xffffffff; + id.s_addr = oi->address->u.prefix4.s_addr; + links += link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, 0, 0); + } + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("PointToMultipoint: running ptomultip_set"); + + /* Search neighbor, */ + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) + if ((nbr = rn->info) != NULL) + /* Ignore myself. */ + if (!IPV4_ADDR_SAME(&nbr->router_id, + &oi->ospf->router_id)) + if (nbr->state == NSM_Full) + + { + links += link_info_set( + s, nbr->router_id, + oi->address->u.prefix4, + LSA_LINK_TYPE_POINTOPOINT, 0, + cost); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "PointToMultipoint: set link to %pI4", + &oi->address->u.prefix4); + } + + return links; +} + +/* Set router-LSA link information. */ +static int router_lsa_link_set(struct stream **s, struct ospf_area *area) +{ + struct listnode *node; + struct ospf_interface *oi; + int links = 0; + + for (ALL_LIST_ELEMENTS_RO(area->oiflist, node, oi)) { + struct interface *ifp = oi->ifp; + + /* Check interface is up, OSPF is enable. */ + if (if_is_operative(ifp)) { + if (oi->state != ISM_Down) { + oi->lsa_pos_beg = links; + /* Describe each link. */ + switch (oi->type) { + case OSPF_IFTYPE_POINTOPOINT: + links += lsa_link_ptop_set(s, oi); + break; + case OSPF_IFTYPE_BROADCAST: + links += lsa_link_broadcast_set(s, oi); + break; + case OSPF_IFTYPE_NBMA: + links += lsa_link_nbma_set(s, oi); + break; + case OSPF_IFTYPE_POINTOMULTIPOINT: + links += lsa_link_ptomp_set(s, oi); + break; + case OSPF_IFTYPE_VIRTUALLINK: + links += + lsa_link_virtuallink_set(s, oi); + break; + case OSPF_IFTYPE_LOOPBACK: + links += lsa_link_loopback_set(s, oi); + } + oi->lsa_pos_end = links; + } + } + } + + return links; +} + +/* Set router-LSA body. */ +void ospf_router_lsa_body_set(struct stream **s, struct ospf_area *area) +{ + unsigned long putp; + uint16_t cnt; + + /* Set flags. */ + stream_putc(*s, router_lsa_flags(area)); + + /* Set Zero fields. */ + stream_putc(*s, 0); + + /* Keep pointer to # links. */ + putp = stream_get_endp(*s); + + /* Forward word */ + stream_putw(*s, 0); + + /* Set all link information. */ + cnt = router_lsa_link_set(s, area); + + /* Set # of links here. */ + stream_putw_at(*s, putp, cnt); +} + +static void ospf_stub_router_timer(struct event *t) +{ + struct ospf_area *area = EVENT_ARG(t); + + area->t_stub_router = NULL; + + SET_FLAG(area->stub_router_state, OSPF_AREA_WAS_START_STUB_ROUTED); + + /* clear stub route state and generate router-lsa refresh, don't + * clobber an administratively set stub-router state though. + */ + if (CHECK_FLAG(area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED)) + return; + + UNSET_FLAG(area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED); + + ospf_router_lsa_update_area(area); +} + +static void ospf_stub_router_check(struct ospf_area *area) +{ + /* area must either be administratively configured to be stub + * or startup-time stub-router must be configured and we must in a + * pre-stub + * state. + */ + if (CHECK_FLAG(area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED)) { + SET_FLAG(area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED); + return; + } + + /* not admin-stubbed, check whether startup stubbing is configured and + * whether it's not been done yet + */ + if (CHECK_FLAG(area->stub_router_state, + OSPF_AREA_WAS_START_STUB_ROUTED)) + return; + + if (area->ospf->stub_router_startup_time + == OSPF_STUB_ROUTER_UNCONFIGURED) { + /* stub-router is hence done forever for this area, even if + * someone + * tries configure it (take effect next restart). + */ + SET_FLAG(area->stub_router_state, + OSPF_AREA_WAS_START_STUB_ROUTED); + return; + } + + /* startup stub-router configured and not yet done */ + SET_FLAG(area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED); + + OSPF_AREA_TIMER_ON(area->t_stub_router, ospf_stub_router_timer, + area->ospf->stub_router_startup_time); +} + +/* Create new router-LSA. */ +static struct ospf_lsa *ospf_router_lsa_new(struct ospf_area *area) +{ + struct ospf *ospf = area->ospf; + struct stream *s; + struct lsa_header *lsah; + struct ospf_lsa *new; + int length; + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type1]: Create router-LSA instance"); + + /* check whether stub-router is desired, and if this is the first + * router LSA. + */ + ospf_stub_router_check(area); + + /* Create a stream for LSA. */ + s = stream_new(OSPF_MAX_LSA_SIZE); + /* Set LSA common header fields. */ + lsa_header_set(s, LSA_OPTIONS_GET(area) | LSA_OPTIONS_NSSA_GET(area), + OSPF_ROUTER_LSA, ospf->router_id, ospf->router_id); + + /* Set router-LSA body fields. */ + ospf_router_lsa_body_set(&s, area); + + /* Set length. */ + length = stream_get_endp(s); + lsah = (struct lsa_header *)STREAM_DATA(s); + lsah->length = htons(length); + + /* Now, create OSPF LSA instance. */ + new = ospf_lsa_new_and_data(length); + + new->area = area; + SET_FLAG(new->flags, OSPF_LSA_SELF | OSPF_LSA_SELF_CHECKED); + new->vrf_id = area->ospf->vrf_id; + + /* Copy LSA data to store, discard stream. */ + memcpy(new->data, lsah, length); + stream_free(s); + + return new; +} + +/* Originate Router-LSA. */ +static struct ospf_lsa *ospf_router_lsa_originate(struct ospf_area *area) +{ + struct ospf_lsa *new; + + if (area->ospf->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type%d]: Graceful Restart in progress, don't originate", + OSPF_ROUTER_LSA); + return NULL; + } + + /* Create new router-LSA instance. */ + if ((new = ospf_router_lsa_new(area)) == NULL) { + zlog_err("%s: ospf_router_lsa_new returned NULL", __func__); + return NULL; + } + + /* Sanity check. */ + if (new->data->adv_router.s_addr == INADDR_ANY) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("LSA[Type1]: AdvRouter is 0, discard"); + ospf_lsa_discard(new); + return NULL; + } + + /* Install LSA to LSDB. */ + new = ospf_lsa_install(area->ospf, NULL, new); + + /* Update LSA origination count. */ + area->ospf->lsa_originate_count++; + + /* Flooding new LSA through area. */ + ospf_flood_through_area(area, NULL, new); + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: Originate router-LSA %p", + new->data->type, &new->data->id, + (void *)new); + ospf_lsa_header_dump(new->data); + } + + return new; +} + +/* Refresh router-LSA. */ +static struct ospf_lsa *ospf_router_lsa_refresh(struct ospf_lsa *lsa) +{ + struct ospf_area *area = lsa->area; + struct ospf_lsa *new; + + /* Sanity check. */ + assert(lsa->data); + + /* Delete LSA from neighbor retransmit-list. */ + ospf_ls_retransmit_delete_nbr_area(area, lsa); + + /* Unregister LSA from refresh-list */ + ospf_refresher_unregister_lsa(area->ospf, lsa); + + /* Create new router-LSA instance. */ + if ((new = ospf_router_lsa_new(area)) == NULL) { + zlog_err("%s: ospf_router_lsa_new returned NULL", __func__); + return NULL; + } + + new->data->ls_seqnum = lsa_seqnum_increment(lsa); + + ospf_lsa_install(area->ospf, NULL, new); + + /* Flood LSA through area. */ + ospf_flood_through_area(area, NULL, new); + + /* Debug logging. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: router-LSA refresh", + new->data->type, &new->data->id); + ospf_lsa_header_dump(new->data); + } + + return NULL; +} + +int ospf_router_lsa_update_area(struct ospf_area *area) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("[router-LSA]: (router-LSA area update)"); + + /* Now refresh router-LSA. */ + if (area->router_lsa_self) + ospf_lsa_refresh(area->ospf, area->router_lsa_self); + /* Newly originate router-LSA. */ + else + ospf_router_lsa_originate(area); + + return 0; +} + +int ospf_router_lsa_update(struct ospf *ospf) +{ + struct listnode *node, *nnode; + struct ospf_area *area; + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("Timer[router-LSA Update]: (timer expire)"); + + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + struct ospf_lsa *lsa = area->router_lsa_self; + struct router_lsa *rl; + const char *area_str; + + /* Keep Area ID string. */ + area_str = AREA_NAME(area); + + /* If LSA not exist in this Area, originate new. */ + if (lsa == NULL) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type1]: Create router-LSA for Area %s", + area_str); + + ospf_router_lsa_originate(area); + } + /* If router-ID is changed, Link ID must change. + First flush old LSA, then originate new. */ + else if (!IPV4_ADDR_SAME(&lsa->data->id, &ospf->router_id)) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type%d:%pI4]: Refresh router-LSA for Area %s", + lsa->data->type, + &lsa->data->id, area_str); + ospf_refresher_unregister_lsa(ospf, lsa); + ospf_lsa_flush_area(lsa, area); + ospf_lsa_unlock(&area->router_lsa_self); + area->router_lsa_self = NULL; + + /* Refresh router-LSA, (not install) and flood through + * area. */ + ospf_router_lsa_update_area(area); + } else { + rl = (struct router_lsa *)lsa->data; + /* Refresh router-LSA, (not install) and flood through + * area. */ + if (rl->flags != ospf->flags) + ospf_router_lsa_update_area(area); + } + } + + return 0; +} + + +/* network-LSA related functions. */ +/* Originate Network-LSA. */ +static void ospf_network_lsa_body_set(struct stream *s, + struct ospf_interface *oi) +{ + struct in_addr mask; + struct route_node *rn; + struct ospf_neighbor *nbr; + + if (OSPF_IF_PARAM(oi, prefix_suppression)) { + mask.s_addr = 0xffffffff; + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type2]: Interface %s network mask set to host mask due prefix-suppression", + oi->ifp->name); + } else { + masklen2ip(oi->address->prefixlen, &mask); + } + stream_put_ipv4(s, mask.s_addr); + + /* The network-LSA lists those routers that are fully adjacent to + the Designated Router; each fully adjacent router is identified by + its OSPF Router ID. The Designated Router includes itself in this + list. RFC2328, Section 12.4.2 */ + + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) + if ((nbr = rn->info) != NULL) + if (nbr->state == NSM_Full || nbr == oi->nbr_self) + stream_put_ipv4(s, nbr->router_id.s_addr); +} + +static struct ospf_lsa *ospf_network_lsa_new(struct ospf_interface *oi) +{ + struct stream *s; + struct ospf_lsa *new; + struct lsa_header *lsah; + struct ospf_if_params *oip; + int length; + + /* If there are no neighbours on this network (the net is stub), + the router does not originate network-LSA (see RFC 12.4.2) */ + if (oi->full_nbrs == 0) + return NULL; + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type2]: Create network-LSA instance"); + + /* Create new stream for LSA. */ + s = stream_new(OSPF_MAX_LSA_SIZE); + lsah = (struct lsa_header *)STREAM_DATA(s); + + lsa_header_set(s, (OPTIONS(oi) | LSA_OPTIONS_GET(oi->area)), + OSPF_NETWORK_LSA, DR(oi), oi->ospf->router_id); + + /* Set network-LSA body fields. */ + ospf_network_lsa_body_set(s, oi); + + /* Set length. */ + length = stream_get_endp(s); + lsah->length = htons(length); + + /* Create OSPF LSA instance. */ + new = ospf_lsa_new_and_data(length); + + new->area = oi->area; + SET_FLAG(new->flags, OSPF_LSA_SELF | OSPF_LSA_SELF_CHECKED); + new->vrf_id = oi->ospf->vrf_id; + + /* Copy LSA to store. */ + memcpy(new->data, lsah, length); + stream_free(s); + + /* Remember prior network LSA sequence numbers, even if we stop + * originating one for this oi, to try avoid re-originating LSAs with a + * prior sequence number, and thus speed up adjency forming & + * convergence. + */ + if ((oip = ospf_lookup_if_params(oi->ifp, oi->address->u.prefix4))) { + new->data->ls_seqnum = oip->network_lsa_seqnum; + new->data->ls_seqnum = lsa_seqnum_increment(new); + } else { + oip = ospf_get_if_params(oi->ifp, oi->address->u.prefix4); + ospf_if_update_params(oi->ifp, oi->address->u.prefix4); + } + oip->network_lsa_seqnum = new->data->ls_seqnum; + + return new; +} + +/* Originate network-LSA. */ +void ospf_network_lsa_update(struct ospf_interface *oi) +{ + struct ospf_lsa *new; + + if (oi->area->ospf->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type%d]: Graceful Restart in progress, don't originate", + OSPF_NETWORK_LSA); + return; + } + + if (oi->network_lsa_self != NULL) { + ospf_lsa_refresh(oi->ospf, oi->network_lsa_self); + return; + } + + /* Create new network-LSA instance. */ + new = ospf_network_lsa_new(oi); + if (new == NULL) + return; + + /* Install LSA to LSDB. */ + new = ospf_lsa_install(oi->ospf, oi, new); + + /* Update LSA origination count. */ + oi->ospf->lsa_originate_count++; + + /* Flooding new LSA through area. */ + ospf_flood_through_area(oi->area, NULL, new); + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: Originate network-LSA %p", + new->data->type, &new->data->id, + (void *)new); + ospf_lsa_header_dump(new->data); + } + + return; +} + +static struct ospf_lsa *ospf_network_lsa_refresh(struct ospf_lsa *lsa) +{ + struct ospf_area *area = lsa->area; + struct ospf_lsa *new, *new2; + struct ospf_if_params *oip; + struct ospf_interface *oi; + + assert(lsa->data); + + /* Retrieve the oi for the network LSA */ + oi = ospf_if_lookup_by_local_addr(area->ospf, NULL, lsa->data->id); + if (oi == NULL) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug( + "LSA[Type%d:%pI4]: network-LSA refresh: no oi found, ick, ignoring.", + lsa->data->type, &lsa->data->id); + ospf_lsa_header_dump(lsa->data); + } + return NULL; + } + + if (oi->state != ISM_DR) + return NULL; + + /* Delete LSA from neighbor retransmit-list. */ + ospf_ls_retransmit_delete_nbr_area(area, lsa); + + /* Unregister LSA from refresh-list */ + ospf_refresher_unregister_lsa(area->ospf, lsa); + + /* Create new network-LSA instance. */ + new = ospf_network_lsa_new(oi); + if (new == NULL) + return NULL; + + oip = ospf_lookup_if_params(oi->ifp, oi->address->u.prefix4); + assert(oip != NULL); + oip->network_lsa_seqnum = new->data->ls_seqnum = + lsa_seqnum_increment(lsa); + + new2 = ospf_lsa_install(area->ospf, oi, new); + + assert(new2 == new); + + /* Flood LSA through aera. */ + ospf_flood_through_area(area, NULL, new); + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: network-LSA refresh", + new->data->type, &new->data->id); + ospf_lsa_header_dump(new->data); + } + + return new; +} + +static void stream_put_ospf_metric(struct stream *s, uint32_t metric_value) +{ + uint32_t metric; + char *mp; + + /* Put 0 metric. TOS metric is not supported. */ + metric = htonl(metric_value); + mp = (char *)&metric; + mp++; + stream_put(s, mp, 3); +} + +/* summary-LSA related functions. */ +static void ospf_summary_lsa_body_set(struct stream *s, struct prefix *p, + uint32_t metric) +{ + struct in_addr mask; + + masklen2ip(p->prefixlen, &mask); + + /* Put Network Mask. */ + stream_put_ipv4(s, mask.s_addr); + + /* Set # TOS. */ + stream_putc(s, (uint8_t)0); + + /* Set metric. */ + stream_put_ospf_metric(s, metric); +} + +static struct ospf_lsa *ospf_summary_lsa_new(struct ospf_area *area, + struct prefix *p, uint32_t metric, + struct in_addr id) +{ + struct stream *s; + struct ospf_lsa *new; + struct lsa_header *lsah; + int length; + + if (id.s_addr == 0xffffffff) { + /* Maybe Link State ID not available. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type%d]: Link ID not available, can't originate", + OSPF_SUMMARY_LSA); + return NULL; + } + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type3]: Create summary-LSA instance"); + + /* Create new stream for LSA. */ + s = stream_new(OSPF_MAX_LSA_SIZE); + lsah = (struct lsa_header *)STREAM_DATA(s); + + lsa_header_set(s, LSA_OPTIONS_GET(area), OSPF_SUMMARY_LSA, id, + area->ospf->router_id); + + /* Set summary-LSA body fields. */ + ospf_summary_lsa_body_set(s, p, metric); + + /* Set length. */ + length = stream_get_endp(s); + lsah->length = htons(length); + + /* Create OSPF LSA instance. */ + new = ospf_lsa_new_and_data(length); + new->area = area; + SET_FLAG(new->flags, OSPF_LSA_SELF | OSPF_LSA_SELF_CHECKED); + new->vrf_id = area->ospf->vrf_id; + + /* Copy LSA to store. */ + memcpy(new->data, lsah, length); + stream_free(s); + + return new; +} + +/* Originate Summary-LSA. */ +static struct ospf_lsa * +ospf_summary_lsa_prepare_and_flood(struct prefix_ipv4 *p, uint32_t metric, + struct ospf_area *area, struct in_addr id) +{ + struct ospf_lsa *new; + + /* Create new summary-LSA instance. */ + if (!(new = ospf_summary_lsa_new(area, (struct prefix *)p, metric, id))) + return NULL; + + /* Instlal LSA to LSDB. */ + new = ospf_lsa_install(area->ospf, NULL, new); + + /* Update LSA origination count. */ + area->ospf->lsa_originate_count++; + + /* Flooding new LSA through area. */ + ospf_flood_through_area(area, NULL, new); + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: Originate summary-LSA %p", + new->data->type, &new->data->id, + (void *)new); + ospf_lsa_header_dump(new->data); + } + + return new; +} + +static struct ospf_lsa *ospf_handle_summarylsa_lsId_chg(struct ospf_area *area, + struct prefix_ipv4 *p, + uint8_t type, + uint32_t metric, + struct in_addr old_id) +{ + struct ospf_lsa *lsa = NULL; + struct ospf_lsa *summary_lsa = NULL; + struct summary_lsa *sl = NULL; + struct ospf_area *old_area = NULL; + struct ospf *ospf = area->ospf; + struct prefix_ipv4 old_prefix; + uint32_t old_metric; + struct in_addr mask; + uint32_t metric_val; + char *metric_buf; + + lsa = ospf_lsdb_lookup_by_id(area->lsdb, type, p->prefix, + ospf->router_id); + + if (!lsa) { + flog_warn(EC_OSPF_LSA_NULL, "(%s): LSA not found", __func__); + return NULL; + } + + sl = (struct summary_lsa *)lsa->data; + + old_area = lsa->area; + old_metric = GET_METRIC(sl->metric); + old_prefix.prefix = sl->header.id; + old_prefix.prefixlen = ip_masklen(sl->mask); + old_prefix.family = AF_INET; + + + /* change the mask */ + masklen2ip(p->prefixlen, &mask); + sl->mask.s_addr = mask.s_addr; + + /* Copy the metric*/ + metric_val = htonl(metric); + metric_buf = (char *)&metric_val; + memcpy(sl->metric, metric_buf, sizeof(metric_val)); + + if (type == OSPF_SUMMARY_LSA) { + /*Refresh the LSA with new LSA*/ + summary_lsa = ospf_summary_lsa_refresh(ospf, lsa); + + ospf_summary_lsa_prepare_and_flood(&old_prefix, old_metric, + old_area, old_id); + } else { + /*Refresh the LSA with new LSA*/ + summary_lsa = ospf_summary_asbr_lsa_refresh(ospf, lsa); + + ospf_asbr_summary_lsa_prepare_and_flood(&old_prefix, old_metric, + old_area, old_id); + } + + return summary_lsa; +} + +/* Originate Summary-LSA. */ +struct ospf_lsa *ospf_summary_lsa_originate(struct prefix_ipv4 *p, + uint32_t metric, + struct ospf_area *area) +{ + struct in_addr id; + enum lsid_status status; + struct ospf_lsa *new = NULL; + + status = ospf_lsa_unique_id(area->ospf, area->lsdb, OSPF_SUMMARY_LSA, p, + &id); + + if (status == LSID_CHANGE) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("Link ID has to be changed."); + + new = ospf_handle_summarylsa_lsId_chg(area, p, OSPF_SUMMARY_LSA, + metric, id); + return new; + } else if (status == LSID_NOT_AVAILABLE) { + /* Link State ID not available. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type5]: Link ID not available, can't originate"); + + return NULL; + } + + new = ospf_summary_lsa_prepare_and_flood(p, metric, area, id); + return new; +} + +static struct ospf_lsa *ospf_summary_lsa_refresh(struct ospf *ospf, + struct ospf_lsa *lsa) +{ + struct ospf_lsa *new; + struct summary_lsa *sl; + struct prefix p; + + /* Sanity check. */ + assert(lsa->data); + + sl = (struct summary_lsa *)lsa->data; + p.prefixlen = ip_masklen(sl->mask); + new = ospf_summary_lsa_new(lsa->area, &p, GET_METRIC(sl->metric), + sl->header.id); + + if (!new) + return NULL; + + new->data->ls_seqnum = lsa_seqnum_increment(lsa); + + ospf_lsa_install(ospf, NULL, new); + + /* Flood LSA through AS. */ + ospf_flood_through_area(new->area, NULL, new); + + /* Debug logging. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: summary-LSA refresh", + new->data->type, &new->data->id); + ospf_lsa_header_dump(new->data); + } + + return new; +} + + +/* summary-ASBR-LSA related functions. */ +static void ospf_summary_asbr_lsa_body_set(struct stream *s, struct prefix *p, + uint32_t metric) +{ + /* Put Network Mask. */ + stream_put_ipv4(s, (uint32_t)0); + + /* Set # TOS. */ + stream_putc(s, (uint8_t)0); + + /* Set metric. */ + stream_put_ospf_metric(s, metric); +} + +static struct ospf_lsa *ospf_summary_asbr_lsa_new(struct ospf_area *area, + struct prefix *p, + uint32_t metric, + struct in_addr id) +{ + struct stream *s; + struct ospf_lsa *new; + struct lsa_header *lsah; + int length; + + if (id.s_addr == 0xffffffff) { + /* Maybe Link State ID not available. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type%d]: Link ID not available, can't originate", + OSPF_ASBR_SUMMARY_LSA); + return NULL; + } + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type3]: Create summary-LSA instance"); + + /* Create new stream for LSA. */ + s = stream_new(OSPF_MAX_LSA_SIZE); + lsah = (struct lsa_header *)STREAM_DATA(s); + + lsa_header_set(s, LSA_OPTIONS_GET(area), OSPF_ASBR_SUMMARY_LSA, id, + area->ospf->router_id); + + /* Set summary-LSA body fields. */ + ospf_summary_asbr_lsa_body_set(s, p, metric); + + /* Set length. */ + length = stream_get_endp(s); + lsah->length = htons(length); + + /* Create OSPF LSA instance. */ + new = ospf_lsa_new_and_data(length); + new->area = area; + SET_FLAG(new->flags, OSPF_LSA_SELF | OSPF_LSA_SELF_CHECKED); + new->vrf_id = area->ospf->vrf_id; + + /* Copy LSA to store. */ + memcpy(new->data, lsah, length); + stream_free(s); + + return new; +} + +/* Originate summary-ASBR-LSA. */ +static struct ospf_lsa * +ospf_asbr_summary_lsa_prepare_and_flood(struct prefix_ipv4 *p, uint32_t metric, + struct ospf_area *area, + struct in_addr id) +{ + struct ospf_lsa *new; + + /* Create new summary-LSA instance. */ + new = ospf_summary_asbr_lsa_new(area, (struct prefix *)p, metric, id); + if (!new) + return NULL; + + /* Install LSA to LSDB. */ + new = ospf_lsa_install(area->ospf, NULL, new); + + /* Update LSA origination count. */ + area->ospf->lsa_originate_count++; + + /* Flooding new LSA through area. */ + ospf_flood_through_area(area, NULL, new); + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: Originate summary-ASBR-LSA %p", + new->data->type, &new->data->id, + (void *)new); + ospf_lsa_header_dump(new->data); + } + + return new; +} + +struct ospf_lsa *ospf_summary_asbr_lsa_originate(struct prefix_ipv4 *p, + uint32_t metric, + struct ospf_area *area) +{ + struct ospf_lsa *new; + struct in_addr id; + enum lsid_status status; + + status = ospf_lsa_unique_id(area->ospf, area->lsdb, + OSPF_ASBR_SUMMARY_LSA, p, &id); + + if (status == LSID_CHANGE) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("Link ID has to be changed."); + + new = ospf_handle_summarylsa_lsId_chg( + area, p, OSPF_ASBR_SUMMARY_LSA, metric, id); + return new; + } else if (status == LSID_NOT_AVAILABLE) { + /* Link State ID not available. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type5]: Link ID not available, can't originate"); + + return NULL; + } + + new = ospf_asbr_summary_lsa_prepare_and_flood(p, metric, area, id); + return new; +} + +static struct ospf_lsa *ospf_summary_asbr_lsa_refresh(struct ospf *ospf, + struct ospf_lsa *lsa) +{ + struct ospf_lsa *new; + struct summary_lsa *sl; + struct prefix p; + bool ind_lsa = false; + + /* Sanity check. */ + assert(lsa->data); + + if (lsa->area->fr_info.indication_lsa_self && + (lsa->area->fr_info.indication_lsa_self == lsa)) + ind_lsa = true; + + sl = (struct summary_lsa *)lsa->data; + p.prefixlen = ip_masklen(sl->mask); + new = ospf_summary_asbr_lsa_new(lsa->area, &p, GET_METRIC(sl->metric), + sl->header.id); + if (!new) + return NULL; + + new->data->ls_seqnum = lsa_seqnum_increment(lsa); + + ospf_lsa_install(ospf, NULL, new); + + /* Flood LSA through area. */ + ospf_flood_through_area(new->area, NULL, new); + + if (ind_lsa) + new->area->fr_info.indication_lsa_self = new; + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: summary-ASBR-LSA refresh", + new->data->type, &new->data->id); + ospf_lsa_header_dump(new->data); + } + + return new; +} + +/* AS-external-LSA related functions. */ + +/* Get nexthop for AS-external-LSAs. Return nexthop if its interface + is connected, else 0*/ +static struct in_addr ospf_external_lsa_nexthop_get(struct ospf *ospf, + struct in_addr nexthop) +{ + struct in_addr fwd; + struct prefix nh; + struct listnode *node; + struct ospf_interface *oi; + + fwd.s_addr = 0; + + if (!nexthop.s_addr) + return fwd; + + /* Check whether nexthop is covered by OSPF network. */ + nh.family = AF_INET; + nh.u.prefix4 = nexthop; + nh.prefixlen = IPV4_MAX_BITLEN; + + /* XXX/SCALE: If there were a lot of oi's on an ifp, then it'd be + * better to make use of the per-ifp table of ois. + */ + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) + if (if_is_operative(oi->ifp)) + if (oi->address->family == AF_INET) + if (prefix_match(oi->address, &nh)) + return nexthop; + + return fwd; +} + +/* NSSA-external-LSA related functions. */ + +/* Get 1st IP connection for Forward Addr */ + +struct in_addr ospf_get_ip_from_ifp(struct ospf_interface *oi) +{ + struct in_addr fwd; + + fwd.s_addr = INADDR_ANY; + + if (if_is_operative(oi->ifp)) + return oi->address->u.prefix4; + + return fwd; +} + +/* Get 1st IP connection for Forward Addr */ +struct in_addr ospf_get_nssa_ip(struct ospf_area *area) +{ + struct in_addr fwd; + struct in_addr best_default; + struct listnode *node; + struct ospf_interface *oi; + + fwd.s_addr = 0; + best_default.s_addr = 0; + + for (ALL_LIST_ELEMENTS_RO(area->ospf->oiflist, node, oi)) { + if (if_is_operative(oi->ifp)) + if (oi->area->external_routing == OSPF_AREA_NSSA) + if (oi->address + && oi->address->family == AF_INET) { + if (best_default.s_addr == INADDR_ANY) + best_default = + oi->address->u.prefix4; + if (oi->area == area) + return oi->address->u.prefix4; + } + } + if (best_default.s_addr != INADDR_ANY) + return best_default; + + return fwd; +} + +int metric_type(struct ospf *ospf, uint8_t src, unsigned short instance) +{ + struct ospf_redist *red; + + red = ospf_redist_lookup(ospf, src, instance); + + return ((!red || red->dmetric.type < 0) ? DEFAULT_METRIC_TYPE + : red->dmetric.type); +} + +int metric_value(struct ospf *ospf, uint8_t src, unsigned short instance) +{ + struct ospf_redist *red; + + red = ospf_redist_lookup(ospf, src, instance); + if (!red || red->dmetric.value < 0) { + if (src == DEFAULT_ROUTE) { + if (ospf->default_originate == DEFAULT_ORIGINATE_ZEBRA) + return DEFAULT_DEFAULT_ORIGINATE_METRIC; + else + return DEFAULT_DEFAULT_ALWAYS_METRIC; + } else if (ospf->default_metric < 0) + return DEFAULT_DEFAULT_METRIC; + else + return ospf->default_metric; + } + + return red->dmetric.value; +} + +/* Set AS-external-LSA body. */ +static void ospf_external_lsa_body_set(struct stream *s, + struct external_info *ei, + struct ospf *ospf) +{ + struct prefix_ipv4 *p = &ei->p; + struct in_addr mask, fwd_addr; + uint32_t mvalue; + int mtype; + int type; + unsigned short instance; + + /* Put Network Mask. */ + masklen2ip(p->prefixlen, &mask); + stream_put_ipv4(s, mask.s_addr); + + /* If prefix is default, specify DEFAULT_ROUTE. */ + type = is_default_prefix4(&ei->p) ? DEFAULT_ROUTE : ei->type; + instance = is_default_prefix4(&ei->p) ? 0 : ei->instance; + + mtype = (ROUTEMAP_METRIC_TYPE(ei) != -1) + ? ROUTEMAP_METRIC_TYPE(ei) + : metric_type(ospf, type, instance); + + mvalue = (ROUTEMAP_METRIC(ei) != -1) + ? ROUTEMAP_METRIC(ei) + : metric_value(ospf, type, instance); + + /* Put type of external metric. */ + stream_putc(s, (mtype == EXTERNAL_METRIC_TYPE_2 ? 0x80 : 0)); + + /* Put 0 metric. TOS metric is not supported. */ + stream_put_ospf_metric(s, mvalue); + + /* Get forwarding address to nexthop if on the Connection List, else 0. + */ + fwd_addr = ospf_external_lsa_nexthop_get(ospf, ei->nexthop); + + /* Put forwarding address. */ + stream_put_ipv4(s, fwd_addr.s_addr); + + /* Put route tag */ + stream_putl(s, ei->tag); +} + +/* Create new external-LSA. */ +static struct ospf_lsa * +ospf_exnl_lsa_prepare_and_flood(struct ospf *ospf, struct external_info *ei, + struct in_addr id) +{ + struct stream *s; + struct lsa_header *lsah; + struct ospf_lsa *new; + int length; + + /* Create new stream for LSA. */ + s = stream_new(OSPF_MAX_LSA_SIZE); + lsah = (struct lsa_header *)STREAM_DATA(s); + + /* Set LSA common header fields. */ + lsa_header_set(s, OSPF_OPTION_E, OSPF_AS_EXTERNAL_LSA, id, + ospf->router_id); + + /* Set AS-external-LSA body fields. */ + ospf_external_lsa_body_set(s, ei, ospf); + + /* Set length. */ + length = stream_get_endp(s); + lsah->length = htons(length); + + /* Now, create OSPF LSA instance. */ + new = ospf_lsa_new_and_data(length); + new->area = NULL; + SET_FLAG(new->flags, + OSPF_LSA_SELF | OSPF_LSA_APPROVED | OSPF_LSA_SELF_CHECKED); + new->vrf_id = ospf->vrf_id; + + /* Copy LSA data to store, discard stream. */ + memcpy(new->data, lsah, length); + stream_free(s); + + return new; +} + +static struct ospf_lsa *ospf_handle_exnl_lsa_lsId_chg(struct ospf *ospf, + struct external_info *ei, + struct in_addr id) +{ + struct ospf_lsa *lsa; + struct as_external_lsa *al; + struct in_addr mask; + struct ospf_lsa *new; + struct external_info ei_summary = {}; + struct external_info *ei_old; + + lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, OSPF_AS_EXTERNAL_LSA, + ei->p.prefix, ospf->router_id); + + if (!lsa) { + flog_warn(EC_OSPF_LSA_NULL, "(%s): LSA not found", __func__); + return NULL; + } + + ei_old = ospf_external_info_check(ospf, lsa); + + al = (struct as_external_lsa *)lsa->data; + + if (!ei_old) { + /* eii_old pointer of LSA is NULL, this + * must be external aggregate route. + */ + ei_summary.p.family = AF_INET; + ei_summary.p.prefix = al->header.id; + ei_summary.p.prefixlen = ip_masklen(al->mask); + ei_summary.tag = (unsigned long)ntohl(al->e[0].route_tag); + ei_old = &ei_summary; + } + + /* change the mask */ + masklen2ip(ei->p.prefixlen, &mask); + al->mask.s_addr = mask.s_addr; + + /*Refresh the LSA with new LSA*/ + ospf_external_lsa_refresh(ospf, lsa, ei, LSA_REFRESH_FORCE, 0); + + /*Originate the old LSA with changed LSID*/ + new = ospf_exnl_lsa_prepare_and_flood(ospf, ei_old, id); + + return new; +} + +static struct ospf_lsa *ospf_external_lsa_new(struct ospf *ospf, + struct external_info *ei, + struct in_addr *old_id) +{ + struct ospf_lsa *new; + struct in_addr id; + enum lsid_status status; + + if (ei == NULL) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type5]: External info is NULL, can't originate"); + return NULL; + } + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type5]: Originate AS-external-LSA instance"); + + /* If old Link State ID is specified, refresh LSA with same ID. */ + if (old_id) + id = *old_id; + /* Get Link State with unique ID. */ + else { + status = ospf_lsa_unique_id(ospf, ospf->lsdb, + OSPF_AS_EXTERNAL_LSA, &ei->p, &id); + + if (status == LSID_CHANGE) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("Link ID has to be changed."); + + new = ospf_handle_exnl_lsa_lsId_chg(ospf, ei, id); + return new; + } else if (status == LSID_NOT_AVAILABLE) { + /* Link State ID not available. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type5]: Link ID not available, can't originate"); + return NULL; + } + } + + new = ospf_exnl_lsa_prepare_and_flood(ospf, ei, id); + + return new; +} + +/* As Type-7 */ +static void ospf_install_flood_nssa(struct ospf *ospf, struct ospf_lsa *lsa) +{ + struct ospf_lsa *new; + struct as_external_lsa *extlsa; + struct ospf_area *area; + struct listnode *node, *nnode; + + /* LSA may be a Type-5 originated via translation of a Type-7 LSA + * which originated from an NSSA area. In which case it should not be + * flooded back to NSSA areas. + */ + if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT)) + return; + + /* NSSA Originate or Refresh (If anyNSSA) + + LSA is self-originated. And just installed as Type-5. + Additionally, install as Type-7 LSDB for every attached NSSA. + + P-Bit controls which ABR performs translation to outside world; If + we are an ABR....do not set the P-bit, because we send the Type-5, + not as the ABR Translator, but as the ASBR owner within the AS! + + If we are NOT ABR, Flood through NSSA as Type-7 w/P-bit set. The + elected ABR Translator will see the P-bit, Translate, and re-flood. + + Later, ABR_TASK and P-bit will scan Type-7 LSDB and translate to + Type-5's to non-NSSA Areas. (it will also attempt a re-install) */ + + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + /* Don't install Type-7 LSA's into nonNSSA area */ + if (area->external_routing != OSPF_AREA_NSSA) + continue; + + /* make lsa duplicate, lock=1 */ + new = ospf_lsa_dup(lsa); + new->area = area; + new->data->type = OSPF_AS_NSSA_LSA; + + /* set P-bit if not ABR */ + if (!IS_OSPF_ABR(ospf)) { + SET_FLAG(new->data->options, OSPF_OPTION_NP); + + /* set non-zero FWD ADDR + + draft-ietf-ospf-nssa-update-09.txt + + if the network between the NSSA AS boundary router and + the + adjacent AS is advertised into OSPF as an internal OSPF + route, + the forwarding address should be the next op address as + is cu + currently done with type-5 LSAs. If the intervening + network is + not adversited into OSPF as an internal OSPF route and + the + type-7 LSA's P-bit is set a forwarding address should be + selected from one of the router's active OSPF interface + addresses + which belong to the NSSA. If no such addresses exist, + then + no type-7 LSA's with the P-bit set should originate from + this + router. */ + + /* kevinm: not updating lsa anymore, just new */ + extlsa = (struct as_external_lsa *)(new->data); + + if (extlsa->e[0].fwd_addr.s_addr == INADDR_ANY) + extlsa->e[0].fwd_addr = ospf_get_nssa_ip( + area); /* this NSSA area in ifp */ + + if (extlsa->e[0].fwd_addr.s_addr == INADDR_ANY) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "LSA[Type-7]: Could not build FWD-ADDR"); + ospf_lsa_discard(new); + return; + } + } + + /* install also as Type-7 */ + ospf_lsa_install(ospf, NULL, + new); /* Remove Old, Lock New = 2 */ + + /* will send each copy, lock=2+n */ + ospf_flood_through_as( + ospf, NULL, new); /* all attached NSSA's, no AS/STUBs */ + } +} + +static struct ospf_lsa *ospf_lsa_translated_nssa_new(struct ospf *ospf, + struct ospf_lsa *type7) +{ + + struct ospf_lsa *new; + struct as_external_lsa *ext, *extnew; + struct external_info ei; + + ext = (struct as_external_lsa *)(type7->data); + + /* need external_info struct, fill in bare minimum */ + ei.p.family = AF_INET; + ei.p.prefix = type7->data->id; + ei.p.prefixlen = ip_masklen(ext->mask); + ei.type = ZEBRA_ROUTE_OSPF; + ei.nexthop = ext->header.adv_router; + ei.route_map_set.metric = -1; + ei.route_map_set.metric_type = -1; + ei.metric = DEFAULT_DEFAULT_METRIC; + ei.max_metric = OSPF_LS_INFINITY; + ei.min_metric = 0; + ei.tag = 0; + ei.instance = 0; + + if ((new = ospf_external_lsa_new(ospf, &ei, &type7->data->id)) + == NULL) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "%s: Could not originate Translated Type-5 for %pI4", + __func__, &ei.p.prefix); + return NULL; + } + + extnew = (struct as_external_lsa *)(new->data); + + /* copy over Type-7 data to new */ + extnew->e[0].tos = ext->e[0].tos; + extnew->e[0].route_tag = ext->e[0].route_tag; + if (type7->area->suppress_fa) { + extnew->e[0].fwd_addr.s_addr = 0; + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: Suppress forwarding address for %pI4", + __func__, &ei.p.prefix); + } else + extnew->e[0].fwd_addr.s_addr = ext->e[0].fwd_addr.s_addr; + new->data->ls_seqnum = type7->data->ls_seqnum; + + /* add translated flag, checksum and lock new lsa */ + SET_FLAG(new->flags, OSPF_LSA_LOCAL_XLT); /* Translated from 7 */ + + return new; +} + +/* Originate Translated Type-5 for supplied Type-7 NSSA LSA */ +struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, + struct ospf_lsa *type7, + struct ospf_lsa *type5) +{ + struct ospf_lsa *new, *translated_lsa; + struct as_external_lsa *extnew; + + if (ospf->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Translated Type5]: Graceful Restart in progress, don't originate"); + return NULL; + } + + /* we cant use ospf_external_lsa_originate() as we need to set + * the OSPF_LSA_LOCAL_XLT flag, must originate by hand + */ + + if ((translated_lsa = ospf_lsa_translated_nssa_new(ospf, type7)) == + NULL) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "%s: Could not translate Type-7, Id %pI4, to Type-5", + __func__, &type7->data->id); + return NULL; + } + + extnew = (struct as_external_lsa *)translated_lsa->data; + + /* Update LSA sequence number from translated Type-5 LSA */ + if (type5) + translated_lsa->data->ls_seqnum = lsa_seqnum_increment(type5); + + if ((new = ospf_lsa_install(ospf, NULL, translated_lsa)) == NULL) { + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "%s: Could not install LSA id %pI4", __func__, + &type7->data->id); + ospf_lsa_free(translated_lsa); + return NULL; + } + + if (IS_DEBUG_OSPF_NSSA) { + zlog_debug("%s: translated Type 7, installed", __func__); + ospf_lsa_header_dump(new->data); + zlog_debug(" Network mask: %d", ip_masklen(extnew->mask)); + zlog_debug(" Forward addr: %pI4", + &extnew->e[0].fwd_addr); + } + + ospf->lsa_originate_count++; + ospf_flood_through_as(ospf, NULL, new); + + return new; +} + +/* Refresh Translated from NSSA AS-external-LSA. */ +struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, + struct ospf_lsa *type7, + struct ospf_lsa *type5) +{ + struct ospf_lsa *new = NULL, *translated_lsa = NULL; + struct as_external_lsa *extold = NULL; + uint32_t ls_seqnum = 0; + + /* Sanity checks. */ + assert(type7 || type5); + if (!(type7 || type5)) + return NULL; + if (type7) + assert(type7->data); + if (type5) + assert(type5->data); + assert(ospf->anyNSSA); + + /* get required data according to what has been given */ + if (type7 && type5 == NULL) { + /* find the translated Type-5 for this Type-7 */ + struct as_external_lsa *ext = + (struct as_external_lsa *)(type7->data); + struct prefix_ipv4 p = { + .prefix = type7->data->id, + .prefixlen = ip_masklen(ext->mask), + .family = AF_INET, + }; + + type5 = ospf_external_info_find_lsa(ospf, &p); + } else if (type5 && type7 == NULL) { + /* find the type-7 from which supplied type-5 was translated, + * ie find first type-7 with same LSA Id. + */ + struct listnode *ln, *lnn; + struct route_node *rn; + struct ospf_lsa *lsa; + struct ospf_area *area; + + for (ALL_LIST_ELEMENTS(ospf->areas, ln, lnn, area)) { + if (area->external_routing != OSPF_AREA_NSSA && !type7) + continue; + + LSDB_LOOP (NSSA_LSDB(area), rn, lsa) { + if (lsa->data->id.s_addr + == type5->data->id.s_addr) { + type7 = lsa; + break; + } + } + } + } + + /* do we have type7? */ + if (!type7) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("%s: no Type-7 found for Type-5 LSA Id %pI4", + __func__, &type5->data->id); + return NULL; + } + + /* do we have valid translated type5? */ + if (type5 == NULL || !CHECK_FLAG(type5->flags, OSPF_LSA_LOCAL_XLT)) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "%s: No translated Type-5 found for Type-7 with Id %pI4", + __func__, &type7->data->id); + return NULL; + } + + extold = (struct as_external_lsa *)type5->data; + if (type7->area->suppress_fa == 1) { + if (extold->e[0].fwd_addr.s_addr == 0) + ls_seqnum = ntohl(type5->data->ls_seqnum); + } + + /* Delete LSA from neighbor retransmit-list. */ + ospf_ls_retransmit_delete_nbr_as(ospf, type5); + + /* create new translated LSA */ + if ((translated_lsa = ospf_lsa_translated_nssa_new(ospf, type7)) == + NULL) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "%s: Could not translate Type-7 for %pI4 to Type-5", + __func__, &type7->data->id); + return NULL; + } + + if (type7->area->suppress_fa == 1) { + if (extold->e[0].fwd_addr.s_addr == 0) + translated_lsa->data->ls_seqnum = htonl(ls_seqnum + 1); + } + + if (!(new = ospf_lsa_install(ospf, NULL, translated_lsa))) { + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "%s: Could not install translated LSA, Id %pI4", + __func__, &type7->data->id); + ospf_lsa_free(translated_lsa); + return NULL; + } + + /* Flood LSA through area. */ + ospf_flood_through_as(ospf, NULL, new); + + return new; +} + +/* Originate an AS-external-LSA, install and flood. */ +struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf, + struct external_info *ei) +{ + struct ospf_lsa *new; + + if (ospf->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type5]: Graceful Restart in progress, don't originate"); + return NULL; + } + + /* Added for NSSA project.... + + External LSAs are originated in ASBRs as usual, but for NSSA + systems. + there is the global Type-5 LSDB and a Type-7 LSDB installed for + every area. The Type-7's are flooded to every IR and every ABR; We + install the Type-5 LSDB so that the normal "refresh" code operates + as usual, and flag them as not used during ASE calculations. The + Type-7 LSDB is used for calculations. Each Type-7 has a Forwarding + Address of non-zero. + + If an ABR is the elected NSSA translator, following SPF and during + the ABR task it will translate all the scanned Type-7's, with P-bit + ON and not-self generated, and translate to Type-5's throughout the + non-NSSA/STUB AS. + + A difference in operation depends whether this ASBR is an ABR + or not. If not an ABR, the P-bit is ON, to indicate that any + elected NSSA-ABR can perform its translation. + + If an ABR, the P-bit is OFF; No ABR will perform translation and + this ASBR will flood the Type-5 LSA as usual. + + For the case where this ASBR is not an ABR, the ASE calculations + are based on the Type-5 LSDB; The Type-7 LSDB exists just to + demonstrate to the user that there are LSA's that belong to any + attached NSSA. + + Finally, it just so happens that when the ABR is translating every + Type-7 into Type-5, it installs it into the Type-5 LSDB as an + approved Type-5 (translated from Type-7); at the end of translation + if any Translated Type-5's remain unapproved, then they must be + flushed from the AS. + + */ + + if (ospf->router_id.s_addr == INADDR_ANY) { + if (ei && IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type5:%pI4]: deferring AS-external-LSA origination, router ID is zero", + &ei->p.prefix); + return NULL; + } + + /* Create new AS-external-LSA instance. */ + if ((new = ospf_external_lsa_new(ospf, ei, NULL)) == NULL) { + if (ei && IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type5:%pI4]: Could not originate AS-external-LSA", + &ei->p.prefix); + return NULL; + } + + /* Install newly created LSA into Type-5 LSDB, lock = 1. */ + ospf_lsa_install(ospf, NULL, new); + + /* Update LSA origination count. */ + ospf->lsa_originate_count++; + + /* Flooding new LSA. only to AS (non-NSSA/STUB) */ + ospf_flood_through_as(ospf, NULL, new); + + /* If there is any attached NSSA, do special handling */ + if (ospf->anyNSSA && + /* stay away from translated LSAs! */ + !(CHECK_FLAG(new->flags, OSPF_LSA_LOCAL_XLT))) + ospf_install_flood_nssa( + ospf, new); /* Install/Flood Type-7 to all NSSAs */ + + /* Debug logging. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: Originate AS-external-LSA %p", + new->data->type, &new->data->id, + (void *)new); + ospf_lsa_header_dump(new->data); + } + + return new; +} + +/* Originate an NSSA-LSA, install and flood. */ +struct ospf_lsa *ospf_nssa_lsa_originate(struct ospf_area *area, + struct external_info *ei) +{ + struct ospf *ospf = area->ospf; + struct ospf_lsa *new; + + if (ospf->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type7]: Graceful Restart in progress, don't originate"); + return NULL; + } + + if (ospf->router_id.s_addr == INADDR_ANY) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type7:%pI4]: deferring NSSA-LSA origination, router ID is zero", + &ei->p.prefix); + return NULL; + } + + /* Create new NSSA-LSA instance. */ + if ((new = ospf_external_lsa_new(ospf, ei, NULL)) == NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type7:%pI4]: Could not originate NSSA-LSA", + &ei->p.prefix); + return NULL; + } + new->data->type = OSPF_AS_NSSA_LSA; + new->area = area; + + /* Install newly created LSA into Type-7 LSDB. */ + ospf_lsa_install(ospf, NULL, new); + + /* Update LSA origination count. */ + ospf->lsa_originate_count++; + + /* Flooding new LSA */ + ospf_flood_through_area(area, NULL, new); + + /* Debug logging. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: Originate NSSA-LSA %p", + new->data->type, &new->data->id, (void *)new); + ospf_lsa_header_dump(new->data); + } + + return new; +} + +/* Refresh NSSA-LSA. */ +struct ospf_lsa *ospf_nssa_lsa_refresh(struct ospf_area *area, + struct ospf_lsa *lsa, + struct external_info *ei) +{ + struct ospf *ospf = area->ospf; + struct ospf_lsa *new; + + /* Delete LSA from neighbor retransmit-list. */ + ospf_ls_retransmit_delete_nbr_as(ospf, lsa); + + /* Unregister AS-external-LSA from refresh-list. */ + ospf_refresher_unregister_lsa(ospf, lsa); + + /* Create new NSSA-LSA instance. */ + if ((new = ospf_external_lsa_new(ospf, ei, NULL)) == NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type7:%pI4]: Could not originate NSSA-LSA", + &ei->p.prefix); + return NULL; + } + new->data->type = OSPF_AS_NSSA_LSA; + new->data->ls_seqnum = lsa_seqnum_increment(lsa); + new->area = area; + + /* Install newly created LSA into Type-7 LSDB. */ + ospf_lsa_install(ospf, NULL, new); + + /* Flooding new LSA */ + ospf_flood_through_area(area, NULL, new); + + /* Debug logging. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: NSSA-LSA refresh", + new->data->type, &new->data->id); + ospf_lsa_header_dump(new->data); + } + + return new; +} + +static struct external_info *ospf_default_external_info(struct ospf *ospf) +{ + int type; + struct prefix_ipv4 p; + struct external_info *default_ei; + int ret = 0; + + p.family = AF_INET; + p.prefix.s_addr = 0; + p.prefixlen = 0; + + default_ei = ospf_external_info_lookup(ospf, DEFAULT_ROUTE, 0, &p); + if (!default_ei) + return NULL; + + /* First, lookup redistributed default route. */ + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { + struct list *ext_list; + + if (type == ZEBRA_ROUTE_OSPF) + continue; + + ext_list = ospf->external[type]; + if (!ext_list) + continue; + + ret = ospf_external_default_routemap_apply_walk(ospf, ext_list, + default_ei); + if (ret) + return default_ei; + } + + return NULL; +} + +void ospf_external_lsa_rid_change(struct ospf *ospf) +{ + struct external_info *ei; + struct ospf_external_aggr_rt *aggr; + struct ospf_lsa *lsa = NULL; + int force; + int type; + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + struct route_node *rn; + struct route_table *rt; + struct list *ext_list; + struct listnode *node; + struct ospf_external *ext; + + ext_list = ospf->external[type]; + if (!ext_list) + continue; + + for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) { + /* Originate As-external-LSA from all type of + * distribute source. + */ + rt = ext->external_info; + if (!rt) + continue; + + for (rn = route_top(rt); rn; rn = route_next(rn)) { + ei = rn->info; + + if (!ei) + continue; + + if (is_default_prefix4(&ei->p)) + continue; + + lsa = ospf_external_info_find_lsa(ospf, &ei->p); + + aggr = ospf_external_aggr_match(ospf, &ei->p); + if (aggr) { + + if (!ospf_redistribute_check(ospf, ei, + NULL)) + continue; + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "Originate Summary LSA after reset/router-ID change"); + + /* Here the LSA is originated as new */ + ospf_originate_summary_lsa(ospf, aggr, + ei); + } else if (lsa) { + /* LSA needs to be refreshed even if + * there is no change in the route + * params if the LSA is in maxage. + */ + if (IS_LSA_MAXAGE(lsa)) + force = LSA_REFRESH_FORCE; + else + force = LSA_REFRESH_IF_CHANGED; + + ospf_external_lsa_refresh(ospf, lsa, + ei, force, 0); + } else { + if (!ospf_redistribute_check(ospf, ei, + NULL)) + continue; + + if (!ospf_external_lsa_originate(ospf, + ei)) + flog_warn( + EC_OSPF_LSA_INSTALL_FAILURE, + "LSA: AS-external-LSA was not originated."); + } + } + } + } + + ei = ospf_default_external_info(ospf); + if (ei && !ospf_external_lsa_originate(ospf, ei)) { + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "LSA: AS-external-LSA for default route was not originated."); + } +} + +/* Flush any NSSA LSAs for given prefix */ +void ospf_nssa_lsa_flush(struct ospf *ospf, struct prefix_ipv4 *p) +{ + struct listnode *node, *nnode; + struct ospf_lsa *lsa = NULL; + struct ospf_area *area; + + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + if (area->external_routing == OSPF_AREA_NSSA) { + lsa = ospf_lsa_lookup(ospf, area, OSPF_AS_NSSA_LSA, + p->prefix, ospf->router_id); + if (!lsa) { + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "LSA: There is no such AS-NSSA-LSA %pFX in LSDB", + p); + continue; + } + ospf_ls_retransmit_delete_nbr_area(area, lsa); + if (!IS_LSA_MAXAGE(lsa)) { + ospf_refresher_unregister_lsa(ospf, lsa); + ospf_lsa_flush_area(lsa, area); + } + } + } +} + +/* Flush an AS-external-LSA from LSDB and routing domain. */ +void ospf_external_lsa_flush(struct ospf *ospf, uint8_t type, + struct prefix_ipv4 *p, + ifindex_t ifindex /*, struct in_addr nexthop */) +{ + struct ospf_lsa *lsa; + + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug("LSA: Flushing AS-external-LSA %pFX", p); + + /* First lookup LSA from LSDB. */ + if (!(lsa = ospf_external_info_find_lsa(ospf, p))) { + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "LSA: There is no such AS-external-LSA %pFX in LSDB", + p); + return; + } + + /* If LSA is selforiginated, not a translated LSA, and there is + * NSSA area, flush Type-7 LSA's at first. + */ + if (IS_LSA_SELF(lsa) && (ospf->anyNSSA) + && !(CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT))) + ospf_nssa_lsa_flush(ospf, p); + + if (!IS_LSA_MAXAGE(lsa)) { + /* Sweep LSA from Link State Retransmit List. */ + ospf_ls_retransmit_delete_nbr_as(ospf, lsa); + + /* Unregister LSA from Refresh queue. */ + ospf_refresher_unregister_lsa(ospf, lsa); + + /* Flush AS-external-LSA through AS. */ + ospf_lsa_flush_as(ospf, lsa); + } + + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug("%s: stop", __func__); +} + +void ospf_external_lsa_refresh_default(struct ospf *ospf) +{ + struct prefix_ipv4 p; + struct external_info *ei; + struct ospf_lsa *lsa; + + p.family = AF_INET; + p.prefixlen = 0; + p.prefix.s_addr = INADDR_ANY; + + ei = ospf_default_external_info(ospf); + lsa = ospf_external_info_find_lsa(ospf, &p); + + if (ei && lsa) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("LSA[Type5:0.0.0.0]: Refresh AS-external-LSA %p", + (void *)lsa); + ospf_external_lsa_refresh(ospf, lsa, ei, LSA_REFRESH_FORCE, + false); + } else if (ei && !lsa) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type5:0.0.0.0]: Originate AS-external-LSA"); + ospf_external_lsa_originate(ospf, ei); + } else if (lsa) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("LSA[Type5:0.0.0.0]: Flush AS-external-LSA"); + ospf_external_lsa_flush(ospf, DEFAULT_ROUTE, &p, 0); + } +} + +void ospf_external_lsa_refresh_type(struct ospf *ospf, uint8_t type, + unsigned short instance, int force) +{ + struct route_node *rn; + struct external_info *ei; + struct ospf_external *ext; + + if (type == DEFAULT_ROUTE) + return; + + ext = ospf_external_lookup(ospf, type, instance); + + if (ext && EXTERNAL_INFO(ext)) { + /* Refresh each redistributed AS-external-LSAs. */ + for (rn = route_top(EXTERNAL_INFO(ext)); rn; + rn = route_next(rn)) { + ei = rn->info; + if (ei) { + if (!is_default_prefix4(&ei->p)) { + struct ospf_lsa *lsa; + struct ospf_external_aggr_rt *aggr; + + aggr = ospf_external_aggr_match(ospf, + &ei->p); + lsa = ospf_external_info_find_lsa( + ospf, &ei->p); + if (aggr) { + /* Check the AS-external-LSA + * should be originated. + */ + if (!ospf_redistribute_check( + ospf, ei, NULL)) { + + ospf_unlink_ei_from_aggr( + ospf, aggr, ei); + continue; + } + + if (IS_DEBUG_OSPF( + lsa, + EXTNL_LSA_AGGR)) + zlog_debug( + "%s: Send Aggreate LSA (%pFX)", + __func__, + &aggr->p); + + ospf_originate_summary_lsa( + ospf, aggr, ei); + + } else if (lsa) { + + if (IS_LSA_MAXAGE(lsa)) + force = LSA_REFRESH_FORCE; + + ospf_external_lsa_refresh( + ospf, lsa, ei, force, + false); + } else { + if (!ospf_redistribute_check( + ospf, ei, NULL)) + continue; + ospf_external_lsa_originate( + ospf, ei); + } + } + } + } + } +} + +/* Refresh AS-external-LSA. */ +struct ospf_lsa *ospf_external_lsa_refresh(struct ospf *ospf, + struct ospf_lsa *lsa, + struct external_info *ei, int force, + bool is_aggr) +{ + struct ospf_lsa *new; + int changed = 0; + + /* Check the AS-external-LSA should be originated. */ + if (!is_aggr) + if (!ospf_redistribute_check(ospf, ei, &changed)) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type%d:%pI4] Could not be refreshed, redist check fail", + lsa->data->type, + &lsa->data->id); + + ospf_external_lsa_flush(ospf, ei->type, &ei->p, + ei->ifindex /*, ei->nexthop */); + return NULL; + } + + if (!changed && !force) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type%d:%pI4]: Not refreshed, not changed/forced", + lsa->data->type, &lsa->data->id); + return NULL; + } + + /* Delete LSA from neighbor retransmit-list. */ + ospf_ls_retransmit_delete_nbr_as(ospf, lsa); + + /* Unregister AS-external-LSA from refresh-list. */ + ospf_refresher_unregister_lsa(ospf, lsa); + + new = ospf_external_lsa_new(ospf, ei, &lsa->data->id); + + if (new == NULL) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug("LSA[Type%d:%pI4]: Could not be refreshed", + lsa->data->type, &lsa->data->id); + return NULL; + } + + new->data->ls_seqnum = lsa_seqnum_increment(lsa); + + ospf_lsa_install(ospf, NULL, new); /* As type-5. */ + + /* Flood LSA through AS. */ + ospf_flood_through_as(ospf, NULL, new); + + /* If any attached NSSA, install as Type-7, flood to all NSSA Areas */ + if (ospf->anyNSSA && !(CHECK_FLAG(new->flags, OSPF_LSA_LOCAL_XLT))) + ospf_install_flood_nssa(ospf, + new); /* Install/Flood per new rules */ + + /* Register self-originated LSA to refresh queue. + * Translated LSAs should not be registered, but refreshed upon + * refresh of the Type-7 + */ + if (!CHECK_FLAG(new->flags, OSPF_LSA_LOCAL_XLT)) + ospf_refresher_register_lsa(ospf, new); + + /* Debug logging. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: AS-external-LSA refresh", + new->data->type, &new->data->id); + ospf_lsa_header_dump(new->data); + } + + return new; +} + + +/* LSA installation functions. */ + +/* Install router-LSA to an area. */ +static struct ospf_lsa * +ospf_router_lsa_install(struct ospf *ospf, struct ospf_lsa *new, int rt_recalc) +{ + struct ospf_area *area = new->area; + + /* RFC 2328 Section 13.2 Router-LSAs and network-LSAs + The entire routing table must be recalculated, starting with + the shortest path calculations for each area (not just the + area whose link-state database has changed). + */ + + if (IS_LSA_SELF(new)) { + + /* Only install LSA if it is originated/refreshed by us. + * If LSA was received by flooding, the RECEIVED flag is set so + * do + * not link the LSA */ + if (CHECK_FLAG(new->flags, OSPF_LSA_RECEIVED)) + return new; /* ignore stale LSA */ + + /* Set self-originated router-LSA. */ + ospf_lsa_unlock(&area->router_lsa_self); + area->router_lsa_self = ospf_lsa_lock(new); + + ospf_refresher_register_lsa(ospf, new); + } + if (rt_recalc) + ospf_spf_calculate_schedule(ospf, SPF_FLAG_ROUTER_LSA_INSTALL); + return new; +} + +/* Install network-LSA to an area. */ +static struct ospf_lsa *ospf_network_lsa_install(struct ospf *ospf, + struct ospf_interface *oi, + struct ospf_lsa *new, + int rt_recalc) +{ + + /* RFC 2328 Section 13.2 Router-LSAs and network-LSAs + The entire routing table must be recalculated, starting with + the shortest path calculations for each area (not just the + area whose link-state database has changed). + */ + if (IS_LSA_SELF(new)) { + /* We supposed that when LSA is originated by us, we pass the + int + for which it was originated. If LSA was received by flooding, + the RECEIVED flag is set, so we do not link the LSA to the + int. */ + if (CHECK_FLAG(new->flags, OSPF_LSA_RECEIVED)) + return new; /* ignore stale LSA */ + + ospf_lsa_unlock(&oi->network_lsa_self); + oi->network_lsa_self = ospf_lsa_lock(new); + ospf_refresher_register_lsa(ospf, new); + } + if (rt_recalc) + ospf_spf_calculate_schedule(ospf, SPF_FLAG_NETWORK_LSA_INSTALL); + + return new; +} + +/* Install summary-LSA to an area. */ +static struct ospf_lsa * +ospf_summary_lsa_install(struct ospf *ospf, struct ospf_lsa *new, int rt_recalc) +{ + if (rt_recalc && !IS_LSA_SELF(new)) { +/* RFC 2328 Section 13.2 Summary-LSAs + The best route to the destination described by the summary- + LSA must be recalculated (see Section 16.5). If this + destination is an AS boundary router, it may also be + necessary to re-examine all the AS-external-LSAs. +*/ + + ospf_spf_calculate_schedule(ospf, SPF_FLAG_SUMMARY_LSA_INSTALL); + } + + if (IS_LSA_SELF(new)) + ospf_refresher_register_lsa(ospf, new); + + return new; +} + +/* Install ASBR-summary-LSA to an area. */ +static struct ospf_lsa *ospf_summary_asbr_lsa_install(struct ospf *ospf, + struct ospf_lsa *new, + int rt_recalc) +{ + if (rt_recalc && !IS_LSA_SELF(new)) { +/* RFC 2328 Section 13.2 Summary-LSAs + The best route to the destination described by the summary- + LSA must be recalculated (see Section 16.5). If this + destination is an AS boundary router, it may also be + necessary to re-examine all the AS-external-LSAs. +*/ + ospf_spf_calculate_schedule(ospf, + SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL); + } + + /* register LSA to refresh-list. */ + if (IS_LSA_SELF(new)) + ospf_refresher_register_lsa(ospf, new); + + return new; +} + +/* Install AS-external-LSA. */ +static struct ospf_lsa *ospf_external_lsa_install(struct ospf *ospf, + struct ospf_lsa *new, + int rt_recalc) +{ + ospf_ase_register_external_lsa(new, ospf); + /* If LSA is not self-originated, calculate an external route. */ + if (rt_recalc) { + /* RFC 2328 Section 13.2 AS-external-LSAs + The best route to the destination described by the AS- + external-LSA must be recalculated (see Section 16.6). + */ + + if (!IS_LSA_SELF(new)) + ospf_ase_incremental_update(ospf, new); + } + + if (new->data->type == OSPF_AS_NSSA_LSA) { + /* There is no point to register selforiginate Type-7 LSA for + * refreshing. We rely on refreshing Type-5 LSA's + */ + if (IS_LSA_SELF(new)) + return new; + else { + /* Try refresh type-5 translated LSA for this LSA, if + * one exists. + * New translations will be taken care of by the + * abr_task. + */ + ospf_translated_nssa_refresh(ospf, new, NULL); + ospf_schedule_abr_task(ospf); + } + } + + /* Register self-originated LSA to refresh queue. + * Leave Translated LSAs alone if NSSA is enabled + */ + if (IS_LSA_SELF(new) && !CHECK_FLAG(new->flags, OSPF_LSA_LOCAL_XLT)) + ospf_refresher_register_lsa(ospf, new); + + return new; +} + +void ospf_discard_from_db(struct ospf *ospf, struct ospf_lsdb *lsdb, + struct ospf_lsa *lsa) +{ + struct ospf_lsa *old; + + if (!lsdb) + return; + + old = ospf_lsdb_lookup(lsdb, lsa); + + if (!old) + return; + + if (old->refresh_list >= 0) + ospf_refresher_unregister_lsa(ospf, old); + + switch (old->data->type) { + case OSPF_AS_EXTERNAL_LSA: + ospf_ase_unregister_external_lsa(old, ospf); + ospf_ls_retransmit_delete_nbr_as(ospf, old); + break; + case OSPF_OPAQUE_AS_LSA: + ospf_ls_retransmit_delete_nbr_as(ospf, old); + break; + case OSPF_AS_NSSA_LSA: + ospf_ls_retransmit_delete_nbr_area(old->area, old); + ospf_ase_unregister_external_lsa(old, ospf); + break; + default: + ospf_ls_retransmit_delete_nbr_area(old->area, old); + break; + } + + ospf_lsa_maxage_delete(ospf, old); + ospf_lsa_discard(old); +} + +struct ospf_lsa *ospf_lsa_install(struct ospf *ospf, struct ospf_interface *oi, + struct ospf_lsa *lsa) +{ + struct ospf_lsa *new = NULL; + struct ospf_lsa *old = NULL; + struct ospf_lsdb *lsdb = NULL; + int rt_recalc; + + /* Set LSDB. */ + switch (lsa->data->type) { + /* kevinm */ + case OSPF_AS_NSSA_LSA: + if (lsa->area) + lsdb = lsa->area->lsdb; + else + lsdb = ospf->lsdb; + break; + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + lsdb = ospf->lsdb; + break; + default: + if (lsa->area) + lsdb = lsa->area->lsdb; + break; + } + + assert(lsdb); + + /* RFC 2328 13.2. Installing LSAs in the database + + Installing a new LSA in the database, either as the result of + flooding or a newly self-originated LSA, may cause the OSPF + routing table structure to be recalculated. The contents of the + new LSA should be compared to the old instance, if present. If + there is no difference, there is no need to recalculate the + routing table. When comparing an LSA to its previous instance, + the following are all considered to be differences in contents: + + o The LSA's Options field has changed. + + o One of the LSA instances has LS age set to MaxAge, and + the other does not. + + o The length field in the LSA header has changed. + + o The body of the LSA (i.e., anything outside the 20-byte + LSA header) has changed. Note that this excludes changes + in LS Sequence Number and LS Checksum. + + */ + /* Look up old LSA and determine if any SPF calculation or incremental + update is needed */ + old = ospf_lsdb_lookup(lsdb, lsa); + + /* Do comparison and record if recalc needed. */ + rt_recalc = 0; + if (old == NULL || ospf_lsa_different(old, lsa, false)) { + /* Ref rfc3623 section 3.2.3 + * Installing new lsa or change in the existing LSA + * or flushing existing LSA leads to topo change + * and trigger SPF caculation. + * So, router should be aborted from HELPER role + * if it is detected as TOPO change. + */ + if (ospf->active_restarter_cnt && + CHECK_LSA_TYPE_1_TO_5_OR_7(lsa->data->type)) { + if (old == NULL || ospf_lsa_different(old, lsa, true)) + ospf_helper_handle_topo_chg(ospf, lsa); + } + + rt_recalc = 1; + } + + /* + Sequence number check (Section 14.1 of rfc 2328) + "Premature aging is used when it is time for a self-originated + LSA's sequence number field to wrap. At this point, the current + LSA instance (having LS sequence number MaxSequenceNumber) must + be prematurely aged and flushed from the routing domain before a + new instance with sequence number equal to InitialSequenceNumber + can be originated. " + */ + + if (ntohl(lsa->data->ls_seqnum) - 1 == OSPF_MAX_SEQUENCE_NUMBER) { + if (ospf_lsa_is_self_originated(ospf, lsa)) { + lsa->data->ls_seqnum = htonl(OSPF_MAX_SEQUENCE_NUMBER); + + if (!IS_LSA_MAXAGE(lsa)) + lsa->flags |= OSPF_LSA_PREMATURE_AGE; + lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + + if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) { + zlog_debug( + "%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( + "%s() got an lsa with seq 0x80000000 that was not self originated. Ignoring", + __func__); + ospf_lsa_header_dump(lsa->data); + } + return old; + } + } + + /* discard old LSA from LSDB */ + if (old != NULL) + ospf_discard_from_db(ospf, lsdb, lsa); + + /* Calculate Checksum if self-originated?. */ + if (IS_LSA_SELF(lsa)) + ospf_lsa_checksum(lsa->data); + + /* Insert LSA to LSDB. */ + ospf_lsdb_add(lsdb, lsa); + lsa->lsdb = lsdb; + + /* Do LSA specific installation process. */ + switch (lsa->data->type) { + case OSPF_ROUTER_LSA: + new = ospf_router_lsa_install(ospf, lsa, rt_recalc); + break; + case OSPF_NETWORK_LSA: + assert(oi); + new = ospf_network_lsa_install(ospf, oi, lsa, rt_recalc); + break; + case OSPF_SUMMARY_LSA: + new = ospf_summary_lsa_install(ospf, lsa, rt_recalc); + break; + case OSPF_ASBR_SUMMARY_LSA: + new = ospf_summary_asbr_lsa_install(ospf, lsa, rt_recalc); + break; + case OSPF_AS_EXTERNAL_LSA: + new = ospf_external_lsa_install(ospf, lsa, rt_recalc); + break; + case OSPF_OPAQUE_LINK_LSA: + if (IS_LSA_SELF(lsa)) + lsa->oi = oi; /* Specify outgoing ospf-interface for + this LSA. */ + else { + /* Incoming "oi" for this LSA has set at LSUpd + * reception. */ + } + /* Fallthrough */ + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + new = ospf_opaque_lsa_install(lsa, rt_recalc); + break; + case OSPF_AS_NSSA_LSA: + new = ospf_external_lsa_install(ospf, lsa, rt_recalc); + default: /* type-6,8,9....nothing special */ + break; + } + + if (new == NULL) + return new; /* Installation failed, cannot proceed further -- + endo. */ + + /* Debug logs. */ + if (IS_DEBUG_OSPF(lsa, LSA_INSTALL)) { + switch (lsa->data->type) { + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + case OSPF_AS_NSSA_LSA: + zlog_debug("LSA[%s]: Install %s", dump_lsa_key(new), + lookup_msg(ospf_lsa_type_msg, + new->data->type, NULL)); + break; + default: + zlog_debug("LSA[%s]: Install %s to Area %pI4", + dump_lsa_key(new), + lookup_msg(ospf_lsa_type_msg, + new->data->type, NULL), + &new->area->area_id); + break; + } + } + + /* + If received LSA' ls_age is MaxAge, or lsa is being prematurely aged + (it's getting flushed out of the area), set LSA on MaxAge LSA list. + */ + if (IS_LSA_MAXAGE(new)) { + if (IS_DEBUG_OSPF(lsa, LSA_INSTALL)) + zlog_debug("LSA[%s]: Install LSA %p, MaxAge", + dump_lsa_key(new), lsa); + ospf_lsa_maxage(ospf, lsa); + } + + return new; +} + + +int ospf_check_nbr_status(struct ospf *ospf) +{ + struct listnode *node, *nnode; + struct ospf_interface *oi; + + for (ALL_LIST_ELEMENTS(ospf->oiflist, node, nnode, oi)) { + struct route_node *rn; + struct ospf_neighbor *nbr; + + if (ospf_if_is_enable(oi)) + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) + if ((nbr = rn->info) != NULL) + if (nbr->state == NSM_Exchange + || nbr->state == NSM_Loading) { + route_unlock_node(rn); + return 0; + } + } + + return 1; +} + + +void ospf_maxage_lsa_remover(struct event *thread) +{ + struct ospf *ospf = EVENT_ARG(thread); + struct ospf_lsa *lsa, *old; + struct route_node *rn; + int reschedule = 0; + + ospf->t_maxage = NULL; + + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug("LSA[MaxAge]: remover Start"); + + reschedule = !ospf_check_nbr_status(ospf); + + if (!reschedule) + for (rn = route_top(ospf->maxage_lsa); rn; + rn = route_next(rn)) { + if ((lsa = rn->info) == NULL) { + continue; + } + + /* There is at least one neighbor from which we still + * await an ack + * for that LSA, so we are not allowed to remove it from + * our lsdb yet + * as per RFC 2328 section 14 para 4 a) */ + if (lsa->retransmit_counter > 0) { + reschedule = 1; + continue; + } + + /* TODO: maybe convert this function to a work-queue */ + if (event_should_yield(thread)) { + OSPF_TIMER_ON(ospf->t_maxage, + ospf_maxage_lsa_remover, 0); + route_unlock_node( + rn); /* route_top/route_next */ + return; + } + + /* Remove LSA from the LSDB */ + if (IS_LSA_SELF(lsa)) + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "LSA[Type%d:%pI4]: LSA 0x%lx is self-originated: ", + lsa->data->type, + &lsa->data->id, + (unsigned long)lsa); + + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "LSA[%s]: MaxAge LSA removed from list", + dump_lsa_key(lsa)); + + if (CHECK_FLAG(lsa->flags, OSPF_LSA_PREMATURE_AGE)) { + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "originating new lsa for lsa %p", + lsa); + ospf_lsa_refresh(ospf, lsa); + } + + /* Remove from lsdb. */ + if (lsa->lsdb) { + old = ospf_lsdb_lookup(lsa->lsdb, lsa); + /* The max age LSA here must be the same + * as the LSA in LSDB + */ + if (old != lsa) { + flog_err(EC_OSPF_LSA_MISSING, + "%s: LSA[%s]: LSA not in LSDB", + __func__, dump_lsa_key(lsa)); + + continue; + } + ospf_discard_from_db(ospf, lsa->lsdb, lsa); + ospf_lsdb_delete(lsa->lsdb, lsa); + } else { + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "%s: LSA[%s]: No associated LSDB!", + __func__, dump_lsa_key(lsa)); + } + } + + /* A MaxAge LSA must be removed immediately from the router's link + state database as soon as both a) it is no longer contained on any + neighbor Link state retransmission lists and b) none of the + router's + neighbors are in states Exchange or Loading. */ + if (reschedule) + OSPF_TIMER_ON(ospf->t_maxage, ospf_maxage_lsa_remover, + ospf->maxage_delay); +} + +/* This function checks whether an LSA with initial sequence number should be + * originated after a wrap in sequence number + */ +void ospf_check_and_gen_init_seq_lsa(struct ospf_interface *oi, + struct ospf_lsa *recv_lsa) +{ + struct ospf_lsa *lsa = NULL; + struct ospf *ospf = oi->ospf; + + lsa = ospf_lsa_lookup_by_header(oi->area, recv_lsa->data); + + if ((lsa == NULL) || (!CHECK_FLAG(lsa->flags, OSPF_LSA_PREMATURE_AGE)) + || (lsa->retransmit_counter != 0)) { + if (IS_DEBUG_OSPF(lsa, LSA)) + zlog_debug( + "Do not generate LSA with initial seqence number."); + return; + } + + ospf_lsa_maxage_delete(ospf, lsa); + + lsa->data->ls_seqnum = lsa_seqnum_increment(lsa); + + ospf_lsa_refresh(ospf, lsa); +} + +void ospf_lsa_maxage_delete(struct ospf *ospf, struct ospf_lsa *lsa) +{ + struct route_node *rn; + struct prefix lsa_prefix; + + memset(&lsa_prefix, 0, sizeof(lsa_prefix)); + lsa_prefix.family = AF_UNSPEC; + lsa_prefix.prefixlen = sizeof(lsa_prefix.u.ptr) * CHAR_BIT; + lsa_prefix.u.ptr = (uintptr_t)lsa; + + if ((rn = route_node_lookup(ospf->maxage_lsa, &lsa_prefix))) { + if (rn->info == lsa) { + UNSET_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE); + ospf_lsa_unlock(&lsa); /* maxage_lsa */ + rn->info = NULL; + route_unlock_node( + rn); /* unlock node because lsa is deleted */ + } + route_unlock_node(rn); /* route_node_lookup */ + } else { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: lsa %s is not found in maxage db.", + __func__, dump_lsa_key(lsa)); + } +} + +/* Add LSA onto the MaxAge list, and schedule for removal. + * This does *not* lead to the LSA being flooded, that must be taken + * care of elsewhere, see, e.g., ospf_lsa_flush* (which are callers of this + * function). + */ +void ospf_lsa_maxage(struct ospf *ospf, struct ospf_lsa *lsa) +{ + struct prefix lsa_prefix; + struct route_node *rn; + + /* When we saw a MaxAge LSA flooded to us, we put it on the list + and schedule the MaxAge LSA remover. */ + if (CHECK_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE)) { + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "LSA[%s]: %p already exists on MaxAge LSA list", + dump_lsa_key(lsa), lsa); + return; + } + + memset(&lsa_prefix, 0, sizeof(lsa_prefix)); + lsa_prefix.family = AF_UNSPEC; + lsa_prefix.prefixlen = sizeof(lsa_prefix.u.ptr) * CHAR_BIT; + lsa_prefix.u.ptr = (uintptr_t)lsa; + + rn = route_node_get(ospf->maxage_lsa, &lsa_prefix); + if (rn->info != NULL) { + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "LSA[%s]: found LSA (%p) in table for LSA %p %d", + dump_lsa_key(lsa), rn->info, + (void *)lsa, lsa_prefix.prefixlen); + route_unlock_node(rn); + } else { + rn->info = ospf_lsa_lock(lsa); + SET_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE); + } + + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug("LSA[%s]: MaxAge LSA remover scheduled.", + dump_lsa_key(lsa)); + + OSPF_TIMER_ON(ospf->t_maxage, ospf_maxage_lsa_remover, + ospf->maxage_delay); +} + +static int ospf_lsa_maxage_walker_remover(struct ospf *ospf, + struct ospf_lsa *lsa) +{ + /* Stay away from any Local Translated Type-7 LSAs */ + if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT)) + return 0; + + if (IS_LSA_MAXAGE(lsa)) + /* Self-originated LSAs should NOT time-out instead, + they're flushed and submitted to the max_age list explicitly. + */ + if (!ospf_lsa_is_self_originated(ospf, lsa)) { + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug("LSA[%s]: is MaxAge", + dump_lsa_key(lsa)); + + switch (lsa->data->type) { + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + /* + * As a general rule, whenever network topology + * has changed + * (due to an LSA removal in this case), routing + * recalculation + * should be triggered. However, this is not + * true for opaque + * LSAs. Even if an opaque LSA instance is going + * to be removed + * from the routing domain, it does not mean a + * change in network + * topology, and thus, routing recalculation is + * not needed here. + */ + break; + case OSPF_AS_EXTERNAL_LSA: + case OSPF_AS_NSSA_LSA: + ospf_ase_incremental_update(ospf, lsa); + break; + default: + ospf_spf_calculate_schedule(ospf, + SPF_FLAG_MAXAGE); + break; + } + ospf_lsa_maxage(ospf, lsa); + } + + if (IS_LSA_MAXAGE(lsa) && !ospf_lsa_is_self_originated(ospf, lsa)) + if (LS_AGE(lsa) > OSPF_LSA_MAXAGE + 30) + printf("Eek! Shouldn't happen!\n"); + + return 0; +} + +/* Periodical check of MaxAge LSA. */ +void ospf_lsa_maxage_walker(struct event *thread) +{ + struct ospf *ospf = EVENT_ARG(thread); + struct route_node *rn; + struct ospf_lsa *lsa; + struct ospf_area *area; + struct listnode *node, *nnode; + + ospf->t_maxage_walker = NULL; + + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + LSDB_LOOP (ROUTER_LSDB(area), rn, lsa) + ospf_lsa_maxage_walker_remover(ospf, lsa); + LSDB_LOOP (NETWORK_LSDB(area), rn, lsa) + ospf_lsa_maxage_walker_remover(ospf, lsa); + LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa) + ospf_lsa_maxage_walker_remover(ospf, lsa); + LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa) + ospf_lsa_maxage_walker_remover(ospf, lsa); + LSDB_LOOP (OPAQUE_AREA_LSDB(area), rn, lsa) + ospf_lsa_maxage_walker_remover(ospf, lsa); + LSDB_LOOP (OPAQUE_LINK_LSDB(area), rn, lsa) + ospf_lsa_maxage_walker_remover(ospf, lsa); + LSDB_LOOP (NSSA_LSDB(area), rn, lsa) + ospf_lsa_maxage_walker_remover(ospf, lsa); + } + + /* for AS-external-LSAs. */ + if (ospf->lsdb) { + LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) + ospf_lsa_maxage_walker_remover(ospf, lsa); + LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa) + ospf_lsa_maxage_walker_remover(ospf, lsa); + } + + OSPF_TIMER_ON(ospf->t_maxage_walker, ospf_lsa_maxage_walker, + OSPF_LSA_MAXAGE_CHECK_INTERVAL); +} + +struct ospf_lsa *ospf_lsa_lookup_by_prefix(struct ospf_lsdb *lsdb, uint8_t type, + struct prefix_ipv4 *p, + struct in_addr router_id) +{ + struct ospf_lsa *lsa; + struct in_addr mask, id; + struct lsa_header_mask { + struct lsa_header header; + struct in_addr mask; + } * hmask; + + lsa = ospf_lsdb_lookup_by_id(lsdb, type, p->prefix, router_id); + if (lsa == NULL) + return NULL; + + masklen2ip(p->prefixlen, &mask); + + hmask = (struct lsa_header_mask *)lsa->data; + + if (mask.s_addr != hmask->mask.s_addr) { + id.s_addr = p->prefix.s_addr | (~mask.s_addr); + lsa = ospf_lsdb_lookup_by_id(lsdb, type, id, router_id); + if (!lsa) + return NULL; + } + + return lsa; +} + +struct ospf_lsa *ospf_lsa_lookup(struct ospf *ospf, struct ospf_area *area, + uint32_t type, struct in_addr id, + struct in_addr adv_router) +{ + if (!ospf) + return NULL; + + switch (type) { + case OSPF_ROUTER_LSA: + case OSPF_NETWORK_LSA: + case OSPF_SUMMARY_LSA: + case OSPF_ASBR_SUMMARY_LSA: + case OSPF_AS_NSSA_LSA: + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + return ospf_lsdb_lookup_by_id(area->lsdb, type, id, adv_router); + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + return ospf_lsdb_lookup_by_id(ospf->lsdb, type, id, adv_router); + default: + break; + } + + return NULL; +} + +struct ospf_lsa *ospf_lsa_lookup_by_id(struct ospf_area *area, uint32_t type, + struct in_addr id) +{ + struct ospf_lsa *lsa; + struct route_node *rn; + + switch (type) { + case OSPF_ROUTER_LSA: + return ospf_lsdb_lookup_by_id(area->lsdb, type, id, id); + case OSPF_NETWORK_LSA: + for (rn = route_top(NETWORK_LSDB(area)); rn; + rn = route_next(rn)) + if ((lsa = rn->info)) + if (IPV4_ADDR_SAME(&lsa->data->id, &id)) { + route_unlock_node(rn); + return lsa; + } + break; + case OSPF_SUMMARY_LSA: + case OSPF_ASBR_SUMMARY_LSA: + /* Currently not used. */ + assert(1); + return ospf_lsdb_lookup_by_id(area->lsdb, type, id, id); + case OSPF_AS_EXTERNAL_LSA: + case OSPF_AS_NSSA_LSA: + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + /* Currently not used. */ + break; + default: + break; + } + + return NULL; +} + +struct ospf_lsa *ospf_lsa_lookup_by_header(struct ospf_area *area, + struct lsa_header *lsah) +{ + struct ospf_lsa *match; + + /* + * Strictly speaking, the LSA-ID field for Opaque-LSAs (type-9/10/11) + * is redefined to have two subfields; opaque-type and opaque-id. + * However, it is harmless to treat the two sub fields together, as if + * they two were forming a unique LSA-ID. + */ + + match = ospf_lsa_lookup(area->ospf, area, lsah->type, lsah->id, + lsah->adv_router); + + if (match == NULL) + if (IS_DEBUG_OSPF(lsa, LSA) == OSPF_DEBUG_LSA) + zlog_debug("LSA[Type%d:%pI4]: Lookup by header, NO MATCH", + lsah->type, &lsah->id); + + return match; +} + +/* return +n, l1 is more recent. + return -n, l2 is more recent. + return 0, l1 and l2 is identical. */ +int ospf_lsa_more_recent(struct ospf_lsa *l1, struct ospf_lsa *l2) +{ + int r; + int x, y; + + if (l1 == NULL && l2 == NULL) + return 0; + if (l1 == NULL) + return -1; + if (l2 == NULL) + return 1; + + /* compare LS sequence number. */ + x = (int)ntohl(l1->data->ls_seqnum); + y = (int)ntohl(l2->data->ls_seqnum); + if (x > y) + return 1; + if (x < y) + return -1; + + /* compare LS checksum. */ + r = ntohs(l1->data->checksum) - ntohs(l2->data->checksum); + if (r) + return r; + + /* compare LS age. */ + if (IS_LSA_MAXAGE(l1) && !IS_LSA_MAXAGE(l2)) + return 1; + else if (!IS_LSA_MAXAGE(l1) && IS_LSA_MAXAGE(l2)) + return -1; + + /* compare LS age with MaxAgeDiff. */ + if (LS_AGE(l1) - LS_AGE(l2) > OSPF_LSA_MAXAGE_DIFF) + return -1; + else if (LS_AGE(l2) - LS_AGE(l1) > OSPF_LSA_MAXAGE_DIFF) + return 1; + + /* LSAs are identical. */ + return 0; +} + +/* + * Check if two LSAs are different. + * + * l1 + * The first LSA to compare. + * + * l2 + * The second LSA to compare. + * + * ignore_rcvd_flag + * When set to true, ignore whether the LSAs were received from the network + * or not. This parameter should be set to true when checking for topology + * changes as part of the Graceful Restart helper neighbor procedures. + * + * Returns: + * true if the LSAs are different, false otherwise. + */ +int ospf_lsa_different(struct ospf_lsa *l1, struct ospf_lsa *l2, + bool ignore_rcvd_flag) +{ + char *p1, *p2; + assert(l1); + assert(l2); + assert(l1->data); + assert(l2->data); + + if (l1->data->options != l2->data->options) + return 1; + + if (IS_LSA_MAXAGE(l1) && !IS_LSA_MAXAGE(l2)) + return 1; + + if (IS_LSA_MAXAGE(l2) && !IS_LSA_MAXAGE(l1)) + return 1; + + if (l1->size != l2->size) + return 1; + + if (l1->size == 0) + return 1; + + if (!ignore_rcvd_flag + && CHECK_FLAG((l1->flags ^ l2->flags), OSPF_LSA_RECEIVED)) + return 1; /* May be a stale LSA in the LSBD */ + + if (l1->size == OSPF_LSA_HEADER_SIZE) + return 0; /* nothing to compare */ + + p1 = (char *)l1->data; + p2 = (char *)l2->data; + + if (memcmp(p1 + OSPF_LSA_HEADER_SIZE, p2 + OSPF_LSA_HEADER_SIZE, + l1->size - OSPF_LSA_HEADER_SIZE) + != 0) + return 1; + + return 0; +} + +int ospf_lsa_flush_schedule(struct ospf *ospf, struct ospf_lsa *lsa) +{ + if (lsa == NULL || !IS_LSA_SELF(lsa)) + return 0; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type%d:%pI4]: Schedule self-originated LSA to FLUSH", + lsa->data->type, &lsa->data->id); + + /* Force given lsa's age to MaxAge. */ + lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + + switch (lsa->data->type) { + /* Opaque wants to be notified of flushes */ + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + ospf_opaque_lsa_refresh(lsa); + break; + default: + ospf_refresher_unregister_lsa(ospf, lsa); + ospf_lsa_flush(ospf, lsa); + break; + } + + return 0; +} + +void ospf_flush_self_originated_lsas_now(struct ospf *ospf) +{ + struct listnode *node, *nnode; + struct listnode *node2, *nnode2; + struct ospf_area *area; + struct ospf_interface *oi; + struct ospf_lsa *lsa; + struct route_node *rn; + struct ospf_if_params *oip; + int need_to_flush_ase = 0; + + ospf->inst_shutdown = 1; + + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + if ((lsa = area->router_lsa_self) != NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type%d:%pI4]: Schedule self-originated LSA to FLUSH", + lsa->data->type, + &lsa->data->id); + + ospf_refresher_unregister_lsa(ospf, lsa); + ospf_lsa_flush_area(lsa, area); + ospf_lsa_unlock(&area->router_lsa_self); + area->router_lsa_self = NULL; + } + + for (ALL_LIST_ELEMENTS(area->oiflist, node2, nnode2, oi)) { + if ((lsa = oi->network_lsa_self) != NULL + && oi->state == ISM_DR && oi->full_nbrs > 0) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type%d:%pI4]: Schedule self-originated LSA to FLUSH", + lsa->data->type, + &lsa->data->id); + + ospf_refresher_unregister_lsa( + ospf, oi->network_lsa_self); + ospf_lsa_flush_area(oi->network_lsa_self, area); + ospf_lsa_unlock(&oi->network_lsa_self); + oi->network_lsa_self = NULL; + + oip = ospf_lookup_if_params( + oi->ifp, oi->address->u.prefix4); + if (oip) + oip->network_lsa_seqnum = htonl( + OSPF_INVALID_SEQUENCE_NUMBER); + } + + if (oi->type != OSPF_IFTYPE_VIRTUALLINK + && area->external_routing == OSPF_AREA_DEFAULT) + need_to_flush_ase = 1; + } + + LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa) + ospf_lsa_flush_schedule(ospf, lsa); + LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa) + ospf_lsa_flush_schedule(ospf, lsa); + LSDB_LOOP (OPAQUE_LINK_LSDB(area), rn, lsa) + ospf_lsa_flush_schedule(ospf, lsa); + LSDB_LOOP (OPAQUE_AREA_LSDB(area), rn, lsa) + ospf_lsa_flush_schedule(ospf, lsa); + } + + if (need_to_flush_ase) { + LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) + ospf_lsa_flush_schedule(ospf, lsa); + LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa) + ospf_lsa_flush_schedule(ospf, lsa); + } + + /* + * Make sure that the MaxAge LSA remover is executed immediately, + * without conflicting to other threads. + */ + if (ospf->t_maxage != NULL) { + EVENT_OFF(ospf->t_maxage); + event_execute(master, ospf_maxage_lsa_remover, ospf, 0, NULL); + } + + return; +} + +/** @brief Function to refresh all the self originated + * LSAs for area, when FR state change happens. + * @param area pointer. + * @return Void. + */ +void ospf_refresh_area_self_lsas(struct ospf_area *area) +{ + struct listnode *node2; + struct listnode *nnode2; + struct ospf_interface *oi; + struct route_node *rn; + struct ospf_lsa *lsa; + + if (!area) + return; + + if (area->router_lsa_self) + ospf_lsa_refresh(area->ospf, area->router_lsa_self); + + for (ALL_LIST_ELEMENTS(area->oiflist, node2, nnode2, oi)) + if (oi->network_lsa_self) + ospf_lsa_refresh(oi->ospf, oi->network_lsa_self); + + LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); + LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); + LSDB_LOOP (OPAQUE_LINK_LSDB(area), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); + LSDB_LOOP (OPAQUE_AREA_LSDB(area), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); + LSDB_LOOP (EXTERNAL_LSDB(area->ospf), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); + LSDB_LOOP (OPAQUE_AS_LSDB(area->ospf), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_refresh(area->ospf, lsa); +} + +/* If there is self-originated LSA, then return 1, otherwise return 0. */ +/* An interface-independent version of ospf_lsa_is_self_originated */ +int ospf_lsa_is_self_originated(struct ospf *ospf, struct ospf_lsa *lsa) +{ + struct listnode *node; + struct ospf_interface *oi; + + /* This LSA is already checked. */ + if (CHECK_FLAG(lsa->flags, OSPF_LSA_SELF_CHECKED)) + return IS_LSA_SELF(lsa); + + /* Make sure LSA is self-checked. */ + SET_FLAG(lsa->flags, OSPF_LSA_SELF_CHECKED); + + /* AdvRouter and Router ID is the same. */ + if (IPV4_ADDR_SAME(&lsa->data->adv_router, &ospf->router_id)) + SET_FLAG(lsa->flags, OSPF_LSA_SELF); + + /* LSA is router-LSA. */ + else if (lsa->data->type == OSPF_ROUTER_LSA + && IPV4_ADDR_SAME(&lsa->data->id, &ospf->router_id)) + SET_FLAG(lsa->flags, OSPF_LSA_SELF); + + /* LSA is network-LSA. Compare Link ID with all interfaces. */ + else if (lsa->data->type == OSPF_NETWORK_LSA) + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + /* Ignore virtual link. */ + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + if (oi->address->family == AF_INET) + if (IPV4_ADDR_SAME( + &lsa->data->id, + &oi->address->u.prefix4)) { + /* to make it easier later */ + SET_FLAG(lsa->flags, + OSPF_LSA_SELF); + return IS_LSA_SELF(lsa); + } + } + + return IS_LSA_SELF(lsa); +} + +/* Get unique Link State ID. */ +enum lsid_status ospf_lsa_unique_id(struct ospf *ospf, struct ospf_lsdb *lsdb, + uint8_t type, struct prefix_ipv4 *p, + struct in_addr *id) +{ + struct ospf_lsa *lsa; + struct in_addr mask; + + *id = p->prefix; + + /* Check existence of LSA instance. */ + lsa = ospf_lsdb_lookup_by_id(lsdb, type, *id, ospf->router_id); + if (lsa) { + struct as_external_lsa *al = + (struct as_external_lsa *)lsa->data; + /* Ref rfc2328,Appendex E.1 + * If router already originated the external lsa with lsid + * as the current prefix, and the masklens are same then + * terminate the LSID algorithem. + */ + if (ip_masklen(al->mask) == p->prefixlen) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "%s: Can't get Link State ID for %pFX", + __func__, p); + /* id.s_addr = 0; */ + id->s_addr = 0xffffffff; + return LSID_NOT_AVAILABLE; + } else if (ip_masklen(al->mask) < p->prefixlen) { + /* Ref rfc2328,Appendex E.2 + * the current prefix masklen is greater than the + * existing LSA, then generate the Link state ID, + * by setting all host bits in prefix addressa and + * originate. + * + * Eg: 1st Route : 10.0.0.0/16 - LSID:10.0.0.0 + * 2nd Route : 10.0.0.0/24 - LSID:10.0.0.255 + */ + masklen2ip(p->prefixlen, &mask); + + id->s_addr = p->prefix.s_addr | (~mask.s_addr); + lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, type, *id, + ospf->router_id); + if (lsa) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "%s: Can't get Link State ID for %pFX", + __func__, p); + id->s_addr = 0xffffffff; + return LSID_NOT_AVAILABLE; + } + } else { + /* Ref rfc2328,Appendex E.3 + * the current prefix masklen is lesser than the + * existing LSA,then the originated LSA has to be + * refreshed by modifying masklen, cost and tag. + * Originate the old route info with new LSID by + * setting the host bits in prefix address. + * + * Eg: 1st Route : 10.0.0.0/24 - LSID:10.0.0.0 + * 2nd Route : 10.0.0.0/16 - ? + * Since 2nd route mask len is less than firstone + * LSID has to be changed. + * 1st route LSID:10.0.0.255 + * 2nd route LSID:10.0.0.0 + */ + id->s_addr = lsa->data->id.s_addr | (~al->mask.s_addr); + lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, type, *id, + ospf->router_id); + if (lsa && (ip_masklen(al->mask) != IPV4_MAX_BITLEN)) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "%s: Can't get Link State ID for %pFX", + __func__, p); + id->s_addr = 0xffffffff; + return LSID_NOT_AVAILABLE; + } + return LSID_CHANGE; + } + } + + return LSID_AVAILABLE; +} + + +#define LSA_ACTION_FLOOD_AREA 1 +#define LSA_ACTION_FLUSH_AREA 2 + +struct lsa_action { + uint8_t action; + struct ospf_area *area; + struct ospf_lsa *lsa; +}; + +static void ospf_lsa_action(struct event *t) +{ + struct lsa_action *data; + + data = EVENT_ARG(t); + + if (IS_DEBUG_OSPF(lsa, LSA) == OSPF_DEBUG_LSA) + zlog_debug("LSA[Action]: Performing scheduled LSA action: %d", + data->action); + + switch (data->action) { + case LSA_ACTION_FLOOD_AREA: + ospf_flood_through_area(data->area, NULL, data->lsa); + break; + case LSA_ACTION_FLUSH_AREA: + ospf_lsa_flush_area(data->lsa, data->area); + break; + } + + ospf_lsa_unlock(&data->lsa); /* Message */ + XFREE(MTYPE_OSPF_MESSAGE, data); +} + +void ospf_schedule_lsa_flood_area(struct ospf_area *area, struct ospf_lsa *lsa) +{ + struct lsa_action *data; + + data = XCALLOC(MTYPE_OSPF_MESSAGE, sizeof(struct lsa_action)); + data->action = LSA_ACTION_FLOOD_AREA; + data->area = area; + data->lsa = ospf_lsa_lock(lsa); /* Message / Flood area */ + + event_add_event(master, ospf_lsa_action, data, 0, NULL); +} + +void ospf_schedule_lsa_flush_area(struct ospf_area *area, struct ospf_lsa *lsa) +{ + struct lsa_action *data; + + data = XCALLOC(MTYPE_OSPF_MESSAGE, sizeof(struct lsa_action)); + data->action = LSA_ACTION_FLUSH_AREA; + data->area = area; + data->lsa = ospf_lsa_lock(lsa); /* Message / Flush area */ + + event_add_event(master, ospf_lsa_action, data, 0, NULL); +} + + +/* LSA Refreshment functions. */ +struct ospf_lsa *ospf_lsa_refresh(struct ospf *ospf, struct ospf_lsa *lsa) +{ + struct external_info *ei; + struct ospf_external_aggr_rt *aggr; + struct ospf_lsa *new = NULL; + struct as_external_lsa *al; + struct prefix_ipv4 p; + + assert(CHECK_FLAG(lsa->flags, OSPF_LSA_SELF)); + assert(IS_LSA_SELF(lsa)); + assert(lsa->lock > 0); + + switch (lsa->data->type) { + /* Router and Network LSAs are processed differently. */ + case OSPF_ROUTER_LSA: + new = ospf_router_lsa_refresh(lsa); + break; + case OSPF_NETWORK_LSA: + new = ospf_network_lsa_refresh(lsa); + break; + case OSPF_SUMMARY_LSA: + new = ospf_summary_lsa_refresh(ospf, lsa); + break; + case OSPF_ASBR_SUMMARY_LSA: + new = ospf_summary_asbr_lsa_refresh(ospf, lsa); + break; + case OSPF_AS_EXTERNAL_LSA: + /* Translated from NSSA Type-5s are refreshed when + * from refresh of Type-7 - do not refresh these directly. + */ + + al = (struct as_external_lsa *)lsa->data; + p.family = AF_INET; + p.prefixlen = ip_masklen(al->mask); + p.prefix = lsa->data->id; + + if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT)) + break; + ei = ospf_external_info_check(ospf, lsa); + if (ei) + new = ospf_external_lsa_refresh( + ospf, lsa, ei, LSA_REFRESH_FORCE, false); + else { + aggr = (struct ospf_external_aggr_rt *) + ospf_extrenal_aggregator_lookup(ospf, &p); + if (aggr) { + struct external_info ei_aggr; + + memset(&ei_aggr, 0, sizeof(ei_aggr)); + ei_aggr.p = aggr->p; + ei_aggr.tag = aggr->tag; + ei_aggr.instance = ospf->instance; + ei_aggr.route_map_set.metric = -1; + ei_aggr.route_map_set.metric_type = -1; + + ospf_external_lsa_refresh(ospf, lsa, &ei_aggr, + LSA_REFRESH_FORCE, true); + SET_FLAG(aggr->flags, + OSPF_EXTERNAL_AGGRT_ORIGINATED); + } else + ospf_lsa_flush_as(ospf, lsa); + } + break; + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + new = ospf_opaque_lsa_refresh(lsa); + break; + default: + break; + } + return new; +} + +void ospf_refresher_register_lsa(struct ospf *ospf, struct ospf_lsa *lsa) +{ + uint16_t index, current_index; + + assert(lsa->lock > 0); + assert(IS_LSA_SELF(lsa)); + + if (lsa->refresh_list < 0) { + int delay; + int min_delay = + ospf->lsa_refresh_timer - (2 * OSPF_LS_REFRESH_JITTER); + int max_delay = + ospf->lsa_refresh_timer - OSPF_LS_REFRESH_JITTER; + + /* We want to refresh the LSA within OSPF_LS_REFRESH_TIME which + * is + * 1800s. Use jitter so that we send the LSA sometime between + * 1680s + * and 1740s. + */ + delay = (frr_weak_random() % (max_delay - min_delay)) + + min_delay; + + current_index = ospf->lsa_refresh_queue.index + + (monotime(NULL) - ospf->lsa_refresher_started) + / OSPF_LSA_REFRESHER_GRANULARITY; + + index = (current_index + delay / OSPF_LSA_REFRESHER_GRANULARITY) + % (OSPF_LSA_REFRESHER_SLOTS); + + if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) + zlog_debug( + "LSA[Refresh:Type%d:%pI4]: age %d, added to index %d", + lsa->data->type, &lsa->data->id, + LS_AGE(lsa), index); + + if (!ospf->lsa_refresh_queue.qs[index]) + ospf->lsa_refresh_queue.qs[index] = list_new(); + + listnode_add(ospf->lsa_refresh_queue.qs[index], + ospf_lsa_lock(lsa)); /* lsa_refresh_queue */ + lsa->refresh_list = index; + + if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) + zlog_debug( + "LSA[Refresh:Type%d:%pI4]: %s: setting refresh_list on lsa %p (slot %d)", + lsa->data->type, &lsa->data->id, __func__, + (void *)lsa, index); + } +} + +void ospf_refresher_unregister_lsa(struct ospf *ospf, struct ospf_lsa *lsa) +{ + assert(lsa->lock > 0); + assert(IS_LSA_SELF(lsa)); + if (lsa->refresh_list >= 0) { + struct list *refresh_list = + ospf->lsa_refresh_queue.qs[lsa->refresh_list]; + listnode_delete(refresh_list, lsa); + if (!listcount(refresh_list)) { + list_delete(&refresh_list); + ospf->lsa_refresh_queue.qs[lsa->refresh_list] = NULL; + } + lsa->refresh_list = -1; + ospf_lsa_unlock(&lsa); /* lsa_refresh_queue */ + } +} + +void ospf_lsa_refresh_walker(struct event *t) +{ + struct list *refresh_list; + struct listnode *node, *nnode; + struct ospf *ospf = EVENT_ARG(t); + struct ospf_lsa *lsa; + int i; + struct list *lsa_to_refresh = list_new(); + bool dna_lsa; + + if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) + zlog_debug("LSA[Refresh]: %s: start", __func__); + + + i = ospf->lsa_refresh_queue.index; + + /* Note: if clock has jumped backwards, then time change could be + negative, + so we are careful to cast the expression to unsigned before taking + modulus. */ + ospf->lsa_refresh_queue.index = + ((unsigned long)(ospf->lsa_refresh_queue.index + + (monotime(NULL) + - ospf->lsa_refresher_started) + / OSPF_LSA_REFRESHER_GRANULARITY)) + % OSPF_LSA_REFRESHER_SLOTS; + + if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) + zlog_debug("LSA[Refresh]: %s: next index %d", __func__, + ospf->lsa_refresh_queue.index); + + for (; i != ospf->lsa_refresh_queue.index; + i = (i + 1) % OSPF_LSA_REFRESHER_SLOTS) { + if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) + zlog_debug("LSA[Refresh]: %s: refresh index %d", + __func__, i); + + refresh_list = ospf->lsa_refresh_queue.qs[i]; + + assert(i >= 0); + + ospf->lsa_refresh_queue.qs[i] = NULL; + + if (refresh_list) { + for (ALL_LIST_ELEMENTS(refresh_list, node, nnode, + lsa)) { + if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) + zlog_debug( + "LSA[Refresh:Type%d:%pI4]: %s: refresh lsa %p (slot %d)", + lsa->data->type, &lsa->data->id, + __func__, (void *)lsa, i); + + assert(lsa->lock > 0); + list_delete_node(refresh_list, node); + lsa->refresh_list = -1; + listnode_add(lsa_to_refresh, lsa); + } + list_delete(&refresh_list); + } + } + + ospf->t_lsa_refresher = NULL; + event_add_timer(master, ospf_lsa_refresh_walker, ospf, + ospf->lsa_refresh_interval, &ospf->t_lsa_refresher); + ospf->lsa_refresher_started = monotime(NULL); + + for (ALL_LIST_ELEMENTS(lsa_to_refresh, node, nnode, lsa)) { + dna_lsa = ospf_check_dna_lsa(lsa); + if (!dna_lsa) { /* refresh only non-DNA LSAs */ + ospf_lsa_refresh(ospf, lsa); + } + assert(lsa->lock > 0); + ospf_lsa_unlock(&lsa); /* lsa_refresh_queue & temp for + * lsa_to_refresh. + */ + } + + list_delete(&lsa_to_refresh); + + if (IS_DEBUG_OSPF(lsa, LSA_REFRESH)) + zlog_debug("LSA[Refresh]: %s: end", __func__); +} + +/* Flush the LSAs for the specific area */ +void ospf_flush_lsa_from_area(struct ospf *ospf, struct in_addr area_id, + int type) +{ + struct ospf_area *area; + struct route_node *rn; + struct ospf_lsa *lsa; + + area = ospf_area_get(ospf, area_id); + + switch (type) { + case OSPF_AS_EXTERNAL_LSA: + if ((area->external_routing == OSPF_AREA_NSSA) || + (area->external_routing == OSPF_AREA_STUB)) { + LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) + if (IS_LSA_SELF(lsa) && + !(CHECK_FLAG(lsa->flags, + OSPF_LSA_LOCAL_XLT))) + ospf_lsa_flush_area(lsa, area); + } + break; + case OSPF_AS_NSSA_LSA: + LSDB_LOOP (NSSA_LSDB(area), rn, lsa) + if (IS_LSA_SELF(lsa)) + ospf_lsa_flush_area(lsa, area); + break; + default: + break; + } +} diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h new file mode 100644 index 0000000..d5ca069 --- /dev/null +++ b/ospfd/ospf_lsa.h @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF Link State Advertisement + * Copyright (C) 1999, 2000 Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPF_LSA_H +#define _ZEBRA_OSPF_LSA_H + +#include "stream.h" + +/* OSPF LSA Default metric values */ +#define DEFAULT_DEFAULT_METRIC 20 +#define DEFAULT_DEFAULT_ORIGINATE_METRIC 10 +#define DEFAULT_DEFAULT_ALWAYS_METRIC 1 +#define DEFAULT_METRIC_TYPE EXTERNAL_METRIC_TYPE_2 + +/* OSPF LSA Range definition. */ +#define OSPF_MIN_LSA 1 /* begin range here */ +#define OSPF_MAX_LSA 12 + +/* OSPF LSA Type definition. */ +#define OSPF_UNKNOWN_LSA 0 +#define OSPF_ROUTER_LSA 1 +#define OSPF_NETWORK_LSA 2 +#define OSPF_SUMMARY_LSA 3 +#define OSPF_ASBR_SUMMARY_LSA 4 +#define OSPF_AS_EXTERNAL_LSA 5 +#define OSPF_GROUP_MEMBER_LSA 6 /* Not supported. */ +#define OSPF_AS_NSSA_LSA 7 +#define OSPF_EXTERNAL_ATTRIBUTES_LSA 8 /* Not supported. */ +#define OSPF_OPAQUE_LINK_LSA 9 +#define OSPF_OPAQUE_AREA_LSA 10 +#define OSPF_OPAQUE_AS_LSA 11 + +#define OSPF_LSA_HEADER_SIZE 20U +#define OSPF_ROUTER_LSA_LINK_SIZE 12U +#define OSPF_ROUTER_LSA_TOS_SIZE 4U +#define OSPF_MAX_LSA_SIZE 1500U + +/* AS-external-LSA refresh method. */ +#define LSA_REFRESH_IF_CHANGED 0 +#define LSA_REFRESH_FORCE 1 + +/* OSPF LSA header. */ +struct lsa_header { + uint16_t ls_age; +#define DO_NOT_AGE 0x8000 + uint8_t options; + uint8_t type; + struct in_addr id; + struct in_addr adv_router; + uint32_t ls_seqnum; + uint16_t checksum; + uint16_t length; +}; + +struct vertex; + +/* OSPF LSA. */ +struct ospf_lsa { + /* LSA origination flag. */ + uint8_t flags; +#define OSPF_LSA_SELF 0x01 +#define OSPF_LSA_SELF_CHECKED 0x02 +#define OSPF_LSA_RECEIVED 0x04 +#define OSPF_LSA_APPROVED 0x08 +#define OSPF_LSA_DISCARD 0x10 +#define OSPF_LSA_LOCAL_XLT 0x20 +#define OSPF_LSA_PREMATURE_AGE 0x40 +#define OSPF_LSA_IN_MAXAGE 0x80 + + /* LSA data. and size */ + struct lsa_header *data; + size_t size; + + /* Received time stamp. */ + struct timeval tv_recv; + + /* Last time it was originated */ + struct timeval tv_orig; + + /* All of reference count, also lock to remove. */ + int lock; + + /* Flags for the SPF calculation. */ + struct vertex *stat; + + /* References to this LSA in neighbor retransmission lists*/ + int retransmit_counter; + + /* Area the LSA belongs to, may be NULL if AS-external-LSA. */ + struct ospf_area *area; + + /* Parent LSDB. */ + struct ospf_lsdb *lsdb; + + /* Related Route. */ + void *route; + + /* Refreshement List or Queue */ + int refresh_list; + + /* For Type-9 Opaque-LSAs */ + struct ospf_interface *oi; + + /* VRF Id */ + vrf_id_t vrf_id; + + /*For topo chg detection in HELPER role*/ + bool to_be_acknowledged; + + /* send maxage with no data */ + bool opaque_zero_len_delete; +}; + +/* OSPF LSA Link Type. */ +#define LSA_LINK_TYPE_POINTOPOINT 1 +#define LSA_LINK_TYPE_TRANSIT 2 +#define LSA_LINK_TYPE_STUB 3 +#define LSA_LINK_TYPE_VIRTUALLINK 4 + +/* OSPF Router LSA Flag. */ +#define ROUTER_LSA_BORDER 0x01 /* The router is an ABR */ +#define ROUTER_LSA_EXTERNAL 0x02 /* The router is an ASBR */ +#define ROUTER_LSA_VIRTUAL 0x04 /* The router has a VL in this area */ +#define ROUTER_LSA_NT 0x10 /* The routers always translates Type-7 */ +#define ROUTER_LSA_SHORTCUT 0x20 /* Shortcut-ABR specific flag */ + +#define IS_ROUTER_LSA_VIRTUAL(x) ((x)->flags & ROUTER_LSA_VIRTUAL) +#define IS_ROUTER_LSA_EXTERNAL(x) ((x)->flags & ROUTER_LSA_EXTERNAL) +#define IS_ROUTER_LSA_BORDER(x) ((x)->flags & ROUTER_LSA_BORDER) +#define IS_ROUTER_LSA_SHORTCUT(x) ((x)->flags & ROUTER_LSA_SHORTCUT) +#define IS_ROUTER_LSA_NT(x) ((x)->flags & ROUTER_LSA_NT) + +/* OSPF Router-LSA Link information. */ +struct router_lsa_link { + struct in_addr link_id; + struct in_addr link_data; + struct { + uint8_t type; + uint8_t tos_count; + uint16_t metric; + } m[1]; +}; + +/* OSPF Router-LSAs structure. */ +#define OSPF_ROUTER_LSA_MIN_SIZE 4U /* w/0 link descriptors */ +/* There is an edge case, when number of links in a Router-LSA may be 0 without + breaking the specification. A router, which has no other links to backbone + area besides one virtual link, will not put any VL descriptor blocks into + the Router-LSA generated for area 0 until a full adjacency over the VL is + reached (RFC2328 12.4.1.3). In this case the Router-LSA initially received + by the other end of the VL will have 0 link descriptor blocks, but soon will + be replaced with the next revision having 1 descriptor block. */ +struct router_lsa { + struct lsa_header header; + uint8_t flags; + uint8_t zero; + uint16_t links; + struct router_link { + struct in_addr link_id; + struct in_addr link_data; + uint8_t type; + uint8_t tos; + uint16_t metric; + } link[1]; +}; + +/* OSPF Network-LSAs structure. */ +#define OSPF_NETWORK_LSA_MIN_SIZE 8U /* w/1 router-ID */ +struct network_lsa { + struct lsa_header header; + struct in_addr mask; + struct in_addr routers[1]; +}; + +/* OSPF Summary-LSAs structure. */ +#define OSPF_SUMMARY_LSA_MIN_SIZE 8U /* w/1 TOS metric block */ +struct summary_lsa { + struct lsa_header header; + struct in_addr mask; + uint8_t tos; + uint8_t metric[3]; +}; + +/* OSPF AS-external-LSAs structure. */ +#define OSPF_AS_EXTERNAL_LSA_MIN_SIZE 16U /* w/1 TOS forwarding block */ +struct as_external_lsa { + struct lsa_header header; + struct in_addr mask; + struct as_route { + uint8_t tos; + uint8_t metric[3]; + struct in_addr fwd_addr; + uint32_t route_tag; + } e[1]; +}; + +enum lsid_status { LSID_AVAILABLE = 0, LSID_CHANGE, LSID_NOT_AVAILABLE }; + +#include "ospfd/ospf_opaque.h" + +/* Macros. */ +#define GET_METRIC(x) get_metric(x) +#define IS_EXTERNAL_METRIC(x) ((x) & 0x80) + +#define GET_AGE(x) (ntohs ((x)->data->ls_age) + time (NULL) - (x)->tv_recv) +#define LS_AGE(x) (OSPF_LSA_MAXAGE < get_age(x) ? OSPF_LSA_MAXAGE : get_age(x)) +#define IS_LSA_SELF(L) (CHECK_FLAG ((L)->flags, OSPF_LSA_SELF)) +#define IS_LSA_MAXAGE(L) (LS_AGE ((L)) == OSPF_LSA_MAXAGE) +#define IS_LSA_MAX_SEQ(L) \ + ((L)->data->ls_seqnum == htonl(OSPF_MAX_SEQUENCE_NUMBER)) + +#define OSPF_LSA_UPDATE_DELAY 2 + +#define CHECK_LSA_TYPE_1_TO_5_OR_7(type) \ + ((type == OSPF_ROUTER_LSA) || (type == OSPF_NETWORK_LSA) \ + || (type == OSPF_SUMMARY_LSA) || (type == OSPF_ASBR_SUMMARY_LSA) \ + || (type == OSPF_AS_EXTERNAL_LSA) || (type == OSPF_AS_NSSA_LSA)) + +#define OSPF_FR_CONFIG(o, a) \ + (o->fr_configured || ((a != NULL) ? a->fr_info.configured : 0)) + +/* Prototypes. */ +/* XXX: Eek, time functions, similar are in lib/thread.c */ +extern struct timeval int2tv(int); +extern struct timeval msec2tv(int); + +extern int get_age(struct ospf_lsa *); +extern uint16_t ospf_lsa_checksum(struct lsa_header *); +extern int ospf_lsa_checksum_valid(struct lsa_header *); +extern int ospf_lsa_refresh_delay(struct ospf_lsa *); + +extern const char *dump_lsa_key(struct ospf_lsa *); +extern uint32_t lsa_seqnum_increment(struct ospf_lsa *); +extern void lsa_header_set(struct stream *, uint8_t, uint8_t, struct in_addr, + struct in_addr); +extern struct ospf_neighbor *ospf_nbr_lookup_ptop(struct ospf_interface *); +extern int ospf_check_nbr_status(struct ospf *); + +/* Prototype for LSA primitive. */ +extern struct ospf_lsa *ospf_lsa_new(void); +extern struct ospf_lsa *ospf_lsa_new_and_data(size_t size); +extern struct ospf_lsa *ospf_lsa_dup(struct ospf_lsa *); +extern void ospf_lsa_free(struct ospf_lsa *); +extern struct ospf_lsa *ospf_lsa_lock(struct ospf_lsa *); +extern void ospf_lsa_unlock(struct ospf_lsa **); +extern void ospf_lsa_discard(struct ospf_lsa *); +extern int ospf_lsa_flush_schedule(struct ospf *, struct ospf_lsa *); +extern struct lsa_header *ospf_lsa_data_new(size_t); +extern struct lsa_header *ospf_lsa_data_dup(struct lsa_header *); +extern void ospf_lsa_data_free(struct lsa_header *); + +/* Prototype for various LSAs */ +extern void ospf_router_lsa_body_set(struct stream **s, struct ospf_area *area); +extern uint8_t router_lsa_flags(struct ospf_area *area); +extern int ospf_router_lsa_update(struct ospf *); +extern int ospf_router_lsa_update_area(struct ospf_area *); + +extern void ospf_network_lsa_update(struct ospf_interface *); + +extern struct ospf_lsa * +ospf_summary_lsa_originate(struct prefix_ipv4 *, uint32_t, struct ospf_area *); +extern struct ospf_lsa *ospf_summary_asbr_lsa_originate(struct prefix_ipv4 *, + uint32_t, + struct ospf_area *); + +extern struct ospf_lsa *ospf_lsa_install(struct ospf *, struct ospf_interface *, + struct ospf_lsa *); + +extern void ospf_nssa_lsa_flush(struct ospf *ospf, struct prefix_ipv4 *p); +extern void ospf_external_lsa_flush(struct ospf *, uint8_t, + struct prefix_ipv4 *, + ifindex_t /* , struct in_addr nexthop */); + +extern struct in_addr ospf_get_ip_from_ifp(struct ospf_interface *); + +extern struct ospf_lsa *ospf_external_lsa_originate(struct ospf *, + struct external_info *); +extern struct ospf_lsa *ospf_nssa_lsa_originate(struct ospf_area *area, + struct external_info *ei); +extern struct ospf_lsa *ospf_nssa_lsa_refresh(struct ospf_area *area, + struct ospf_lsa *lsa, + struct external_info *ei); +extern void ospf_external_lsa_rid_change(struct ospf *ospf); +extern struct ospf_lsa *ospf_lsa_lookup(struct ospf *ospf, struct ospf_area *, + uint32_t, struct in_addr, + struct in_addr); +extern struct ospf_lsa *ospf_lsa_lookup_by_id(struct ospf_area *, uint32_t, + struct in_addr); +extern struct ospf_lsa *ospf_lsa_lookup_by_header(struct ospf_area *, + struct lsa_header *); +extern int ospf_lsa_more_recent(struct ospf_lsa *, struct ospf_lsa *); +extern int ospf_lsa_different(struct ospf_lsa *, struct ospf_lsa *, + bool ignore_rcvd_flag); +extern void ospf_flush_self_originated_lsas_now(struct ospf *); + +extern int ospf_lsa_is_self_originated(struct ospf *, struct ospf_lsa *); + +extern struct ospf_lsa *ospf_lsa_lookup_by_prefix(struct ospf_lsdb *, uint8_t, + struct prefix_ipv4 *, + struct in_addr); + +extern void ospf_lsa_maxage(struct ospf *, struct ospf_lsa *); +extern uint32_t get_metric(uint8_t *); + +extern void ospf_lsa_maxage_walker(struct event *thread); +extern struct ospf_lsa *ospf_lsa_refresh(struct ospf *, struct ospf_lsa *); + +extern void ospf_external_lsa_refresh_default(struct ospf *); + +extern void ospf_external_lsa_refresh_type(struct ospf *, uint8_t, + unsigned short, int); +extern struct ospf_lsa *ospf_external_lsa_refresh(struct ospf *, + struct ospf_lsa *, + struct external_info *, int, + bool aggr); +extern enum lsid_status ospf_lsa_unique_id(struct ospf *ospf, + struct ospf_lsdb *lsdb, + uint8_t type, struct prefix_ipv4 *p, + struct in_addr *addr); +extern void ospf_schedule_lsa_flood_area(struct ospf_area *, struct ospf_lsa *); +extern void ospf_schedule_lsa_flush_area(struct ospf_area *, struct ospf_lsa *); + +extern void ospf_refresher_register_lsa(struct ospf *, struct ospf_lsa *); +extern void ospf_refresher_unregister_lsa(struct ospf *, struct ospf_lsa *); +extern void ospf_lsa_refresh_walker(struct event *thread); + +extern void ospf_lsa_maxage_delete(struct ospf *, struct ospf_lsa *); + +extern void ospf_discard_from_db(struct ospf *, struct ospf_lsdb *, + struct ospf_lsa *); + +extern int metric_type(struct ospf *, uint8_t, unsigned short); +extern int metric_value(struct ospf *, uint8_t, unsigned short); + +extern char link_info_set(struct stream **s, struct in_addr id, + struct in_addr data, uint8_t type, uint8_t tos, + uint16_t cost); + +extern struct in_addr ospf_get_nssa_ip(struct ospf_area *); +extern int ospf_translated_nssa_compare(struct ospf_lsa *, struct ospf_lsa *); +extern struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, + struct ospf_lsa *type7, + struct ospf_lsa *type5); +extern struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, + struct ospf_lsa *type7, + struct ospf_lsa *type5); +extern void ospf_check_and_gen_init_seq_lsa(struct ospf_interface *oi, + struct ospf_lsa *lsa); +extern void ospf_flush_lsa_from_area(struct ospf *ospf, struct in_addr area_id, + int type); +extern void ospf_maxage_lsa_remover(struct event *thread); +extern bool ospf_check_dna_lsa(const struct ospf_lsa *lsa); +extern void ospf_refresh_area_self_lsas(struct ospf_area *area); + +/** @brief Check if the LSA is an indication LSA. + * @param lsa pointer. + * @return true or false based on lsa info. + */ +static inline bool ospf_check_indication_lsa(struct ospf_lsa *lsa) +{ + struct summary_lsa *sl = NULL; + + if (lsa->data->type == OSPF_ASBR_SUMMARY_LSA) { + sl = (struct summary_lsa *)lsa->data; + if ((GET_METRIC(sl->metric) == OSPF_LS_INFINITY) && + !CHECK_FLAG(lsa->data->options, OSPF_OPTION_DC)) + return true; + } + + return false; +} +#endif /* _ZEBRA_OSPF_LSA_H */ diff --git a/ospfd/ospf_lsdb.c b/ospfd/ospf_lsdb.c new file mode 100644 index 0000000..0111c49 --- /dev/null +++ b/ospfd/ospf_lsdb.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF LSDB support. + * Copyright (C) 1999, 2000 Alex Zinin, Kunihiro Ishiguro, Toshiaki Takada + */ + +#include + +#include "prefix.h" +#include "table.h" +#include "memory.h" +#include "log.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" + +struct ospf_lsdb *ospf_lsdb_new(void) +{ + struct ospf_lsdb *new; + + new = XCALLOC(MTYPE_OSPF_LSDB, sizeof(struct ospf_lsdb)); + ospf_lsdb_init(new); + + return new; +} + +void ospf_lsdb_init(struct ospf_lsdb *lsdb) +{ + int i; + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) + lsdb->type[i].db = route_table_init(); +} + +void ospf_lsdb_free(struct ospf_lsdb *lsdb) +{ + ospf_lsdb_cleanup(lsdb); + XFREE(MTYPE_OSPF_LSDB, lsdb); +} + +void ospf_lsdb_cleanup(struct ospf_lsdb *lsdb) +{ + int i; + assert(lsdb); + assert(lsdb->total == 0); + + ospf_lsdb_delete_all(lsdb); + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) + route_table_finish(lsdb->type[i].db); +} + +void ls_prefix_set(struct prefix_ls *lp, struct ospf_lsa *lsa) +{ + if (lp && lsa && lsa->data) { + lp->family = AF_UNSPEC; + lp->prefixlen = 64; + lp->id = lsa->data->id; + lp->adv_router = lsa->data->adv_router; + } +} + +static void ospf_lsdb_delete_entry(struct ospf_lsdb *lsdb, + struct route_node *rn) +{ + struct ospf_lsa *lsa = rn->info; + + if (!lsa) + return; + + assert(rn->table == lsdb->type[lsa->data->type].db); + + if (IS_LSA_SELF(lsa)) + lsdb->type[lsa->data->type].count_self--; + lsdb->type[lsa->data->type].count--; + lsdb->type[lsa->data->type].checksum -= ntohs(lsa->data->checksum); + lsdb->total--; + + /* Decrement number of router LSAs received with DC bit set */ + if (lsa->area && (lsa->area->lsdb == lsdb) && !IS_LSA_SELF(lsa) && + (lsa->data->type == OSPF_ROUTER_LSA) && + CHECK_FLAG(lsa->data->options, OSPF_OPTION_DC)) + lsa->area->fr_info.router_lsas_recv_dc_bit--; + + /* + * If the LSA being deleted is indication LSA, then set the + * pointer to NULL. + */ + if (lsa->area && lsa->area->fr_info.indication_lsa_self && + (lsa->area->fr_info.indication_lsa_self == lsa)) + lsa->area->fr_info.indication_lsa_self = NULL; + + rn->info = NULL; + route_unlock_node(rn); +#ifdef MONITOR_LSDB_CHANGE + if (lsdb->del_lsa_hook != NULL) + (*lsdb->del_lsa_hook)(lsa); +#endif /* MONITOR_LSDB_CHANGE */ + ospf_lsa_unlock(&lsa); /* lsdb */ + return; +} + +/* Add new LSA to lsdb. */ +void ospf_lsdb_add(struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) +{ + struct route_table *table; + struct prefix_ls lp; + struct route_node *rn; + + table = lsdb->type[lsa->data->type].db; + ls_prefix_set(&lp, lsa); + rn = route_node_get(table, (struct prefix *)&lp); + + /* nothing to do? */ + if (rn->info && rn->info == lsa) { + route_unlock_node(rn); + return; + } + + /* purge old entry? */ + if (rn->info) + ospf_lsdb_delete_entry(lsdb, rn); + + if (IS_LSA_SELF(lsa)) + lsdb->type[lsa->data->type].count_self++; + lsdb->type[lsa->data->type].count++; + lsdb->total++; + + /* Increment number of router LSAs received with DC bit set */ + if (lsa->area && (lsa->area->lsdb == lsdb) && !IS_LSA_SELF(lsa) && + (lsa->data->type == OSPF_ROUTER_LSA) && + CHECK_FLAG(lsa->data->options, OSPF_OPTION_DC)) + lsa->area->fr_info.router_lsas_recv_dc_bit++; + +#ifdef MONITOR_LSDB_CHANGE + if (lsdb->new_lsa_hook != NULL) + (*lsdb->new_lsa_hook)(lsa); +#endif /* MONITOR_LSDB_CHANGE */ + lsdb->type[lsa->data->type].checksum += ntohs(lsa->data->checksum); + rn->info = ospf_lsa_lock(lsa); /* lsdb */ +} + +void ospf_lsdb_delete(struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) +{ + struct route_table *table; + struct prefix_ls lp; + struct route_node *rn; + + if (!lsdb || !lsa) + return; + + assert(lsa->data->type < OSPF_MAX_LSA); + table = lsdb->type[lsa->data->type].db; + ls_prefix_set(&lp, lsa); + if ((rn = route_node_lookup(table, (struct prefix *)&lp))) { + if (rn->info == lsa) + ospf_lsdb_delete_entry(lsdb, rn); + route_unlock_node(rn); /* route_node_lookup */ + } +} + +void ospf_lsdb_delete_all(struct ospf_lsdb *lsdb) +{ + struct route_table *table; + struct route_node *rn; + int i; + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) { + table = lsdb->type[i].db; + for (rn = route_top(table); rn; rn = route_next(rn)) + if (rn->info != NULL) + ospf_lsdb_delete_entry(lsdb, rn); + } +} + +struct ospf_lsa *ospf_lsdb_lookup(struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) +{ + struct route_table *table; + struct prefix_ls lp; + struct route_node *rn; + struct ospf_lsa *find; + + table = lsdb->type[lsa->data->type].db; + ls_prefix_set(&lp, lsa); + rn = route_node_lookup(table, (struct prefix *)&lp); + if (rn) { + find = rn->info; + route_unlock_node(rn); + return find; + } + return NULL; +} + +struct ospf_lsa *ospf_lsdb_lookup_by_id(struct ospf_lsdb *lsdb, uint8_t type, + struct in_addr id, + struct in_addr adv_router) +{ + struct route_table *table; + struct prefix_ls lp; + struct route_node *rn; + struct ospf_lsa *find; + + table = lsdb->type[type].db; + + memset(&lp, 0, sizeof(lp)); + lp.family = AF_UNSPEC; + lp.prefixlen = 64; + lp.id = id; + lp.adv_router = adv_router; + + rn = route_node_lookup(table, (struct prefix *)&lp); + if (rn) { + find = rn->info; + route_unlock_node(rn); + return find; + } + return NULL; +} + +struct ospf_lsa *ospf_lsdb_lookup_by_id_next(struct ospf_lsdb *lsdb, + uint8_t type, struct in_addr id, + struct in_addr adv_router, + int first) +{ + struct route_table *table; + struct prefix_ls lp; + struct route_node *rn; + struct ospf_lsa *find; + + table = lsdb->type[type].db; + + memset(&lp, 0, sizeof(lp)); + lp.family = AF_UNSPEC; + lp.prefixlen = 64; + lp.id = id; + lp.adv_router = adv_router; + + if (first) + rn = route_top(table); + else { + if ((rn = route_node_lookup(table, (struct prefix *)&lp)) + == NULL) + return NULL; + rn = route_next(rn); + } + + for (; rn; rn = route_next(rn)) + if (rn->info) + break; + + if (rn && rn->info) { + find = rn->info; + route_unlock_node(rn); + return find; + } + return NULL; +} + +unsigned long ospf_lsdb_count_all(struct ospf_lsdb *lsdb) +{ + return lsdb->total; +} + +unsigned long ospf_lsdb_count(struct ospf_lsdb *lsdb, int type) +{ + return lsdb->type[type].count; +} + +unsigned long ospf_lsdb_count_self(struct ospf_lsdb *lsdb, int type) +{ + return lsdb->type[type].count_self; +} + +unsigned int ospf_lsdb_checksum(struct ospf_lsdb *lsdb, int type) +{ + return lsdb->type[type].checksum; +} + +unsigned long ospf_lsdb_isempty(struct ospf_lsdb *lsdb) +{ + return (lsdb->total == 0); +} diff --git a/ospfd/ospf_lsdb.h b/ospfd/ospf_lsdb.h new file mode 100644 index 0000000..bf295ca --- /dev/null +++ b/ospfd/ospf_lsdb.h @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF LSDB support. + * Copyright (C) 1999, 2000 Alex Zinin, Kunihiro Ishiguro, Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPF_LSDB_H +#define _ZEBRA_OSPF_LSDB_H + +/* OSPF LSDB structure. */ +struct ospf_lsdb { + struct { + unsigned long count; + unsigned long count_self; + unsigned int checksum; + struct route_table *db; + } type[OSPF_MAX_LSA]; + unsigned long total; +#define MONITOR_LSDB_CHANGE 1 /* XXX */ +#ifdef MONITOR_LSDB_CHANGE + /* Hooks for callback functions to catch every add/del event. */ + int (*new_lsa_hook)(struct ospf_lsa *); + int (*del_lsa_hook)(struct ospf_lsa *); +#endif /* MONITOR_LSDB_CHANGE */ +}; + +/* Macros. */ +#define LSDB_LOOP(T, N, L) \ + if ((T) != NULL) \ + for ((N) = route_top((T)); ((N)); ((N)) = route_next((N))) \ + if (((L) = (N)->info)) + +#define ROUTER_LSDB(A) ((A)->lsdb->type[OSPF_ROUTER_LSA].db) +#define NETWORK_LSDB(A) ((A)->lsdb->type[OSPF_NETWORK_LSA].db) +#define SUMMARY_LSDB(A) ((A)->lsdb->type[OSPF_SUMMARY_LSA].db) +#define ASBR_SUMMARY_LSDB(A) ((A)->lsdb->type[OSPF_ASBR_SUMMARY_LSA].db) +#define EXTERNAL_LSDB(O) ((O)->lsdb->type[OSPF_AS_EXTERNAL_LSA].db) +#define NSSA_LSDB(A) ((A)->lsdb->type[OSPF_AS_NSSA_LSA].db) +#define OPAQUE_LINK_LSDB(A) ((A)->lsdb->type[OSPF_OPAQUE_LINK_LSA].db) +#define OPAQUE_AREA_LSDB(A) ((A)->lsdb->type[OSPF_OPAQUE_AREA_LSA].db) +#define OPAQUE_AS_LSDB(O) ((O)->lsdb->type[OSPF_OPAQUE_AS_LSA].db) + +#define AREA_LSDB(A,T) ((A)->lsdb->type[(T)].db) +#define AS_LSDB(O,T) ((O)->lsdb->type[(T)].db) + +/* OSPF LSDB related functions. */ +extern struct ospf_lsdb *ospf_lsdb_new(void); +extern void ospf_lsdb_init(struct ospf_lsdb *); +extern void ospf_lsdb_free(struct ospf_lsdb *); +extern void ospf_lsdb_cleanup(struct ospf_lsdb *); +extern void ls_prefix_set(struct prefix_ls *lp, struct ospf_lsa *lsa); +extern void ospf_lsdb_add(struct ospf_lsdb *, struct ospf_lsa *); +extern void ospf_lsdb_delete(struct ospf_lsdb *, struct ospf_lsa *); +extern void ospf_lsdb_delete_all(struct ospf_lsdb *); +extern struct ospf_lsa *ospf_lsdb_lookup(struct ospf_lsdb *, struct ospf_lsa *); +extern struct ospf_lsa *ospf_lsdb_lookup_by_id(struct ospf_lsdb *, uint8_t, + struct in_addr, struct in_addr); +extern struct ospf_lsa *ospf_lsdb_lookup_by_id_next(struct ospf_lsdb *, uint8_t, + struct in_addr, + struct in_addr, int); +extern unsigned long ospf_lsdb_count_all(struct ospf_lsdb *); +extern unsigned long ospf_lsdb_count(struct ospf_lsdb *, int); +extern unsigned long ospf_lsdb_count_self(struct ospf_lsdb *, int); +extern unsigned int ospf_lsdb_checksum(struct ospf_lsdb *, int); +extern unsigned long ospf_lsdb_isempty(struct ospf_lsdb *); + +#endif /* _ZEBRA_OSPF_LSDB_H */ diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c new file mode 100644 index 0000000..dd00024 --- /dev/null +++ b/ospfd/ospf_main.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPFd main routine. + * Copyright (C) 1998, 99 Kunihiro Ishiguro, Toshiaki Takada + */ + +#include + +#include +#include "bfd.h" +#include "getopt.h" +#include "frrevent.h" +#include "prefix.h" +#include "linklist.h" +#include "if.h" +#include "vector.h" +#include "vty.h" +#include "command.h" +#include "filter.h" +#include "plist.h" +#include "stream.h" +#include "log.h" +#include "memory.h" +#include "privs.h" +#include "sigevent.h" +#include "zclient.h" +#include "vrf.h" +#include "libfrr.h" +#include "routemap.h" +#include "keychain.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_vty.h" +#include "ospfd/ospf_bfd.h" +#include "ospfd/ospf_gr.h" +#include "ospfd/ospf_errors.h" +#include "ospfd/ospf_ldp_sync.h" +#include "ospfd/ospf_routemap_nb.h" + +/* ospfd privileges */ +zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, + ZCAP_SYS_ADMIN}; + +struct zebra_privs_t ospfd_privs = { +#if defined(FRR_USER) && defined(FRR_GROUP) + .user = FRR_USER, + .group = FRR_GROUP, +#endif +#if defined(VTY_GROUP) + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0}; + +/* OSPFd options. */ +const struct option longopts[] = { + {"instance", required_argument, NULL, 'n'}, + {"apiserver", no_argument, NULL, 'a'}, + {0} +}; + +/* OSPFd program name */ + +/* Master of threads. */ +struct event_loop *master; + +#ifdef SUPPORT_OSPF_API +extern int ospf_apiserver_enable; +#endif /* SUPPORT_OSPF_API */ + +/* SIGHUP handler. */ +static void sighup(void) +{ + zlog_info("SIGHUP received"); +} + +/* SIGINT / SIGTERM handler. */ +static void sigint(void) +{ + zlog_notice("Terminating on signal"); + bfd_protocol_integration_set_shutdown(true); + ospf_terminate(); + exit(0); +} + +/* SIGUSR1 handler. */ +static void sigusr1(void) +{ + zlog_rotate(); +} + +struct frr_signal_t ospf_signals[] = { + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + +static const struct frr_yang_module_info *const ospfd_yang_modules[] = { + &frr_filter_info, + &frr_interface_info, + &frr_route_map_info, + &frr_vrf_info, + &frr_ospf_route_map_info, +}; + +FRR_DAEMON_INFO(ospfd, OSPF, .vty_port = OSPF_VTY_PORT, + + .proghelp = "Implementation of the OSPFv2 routing protocol.", + + .signals = ospf_signals, .n_signals = array_size(ospf_signals), + + .privs = &ospfd_privs, .yang_modules = ospfd_yang_modules, + .n_yang_modules = array_size(ospfd_yang_modules), +); + +/** Max wait time for config to load before accepting hellos */ +#define OSPF_PRE_CONFIG_MAX_WAIT_SECONDS 600 + +static void ospf_config_finish(struct event *t) +{ + zlog_err("OSPF configuration end timer expired after %d seconds.", + OSPF_PRE_CONFIG_MAX_WAIT_SECONDS); +} + +static void ospf_config_start(void) +{ + EVENT_OFF(t_ospf_cfg); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("ospfd config start callback received."); + event_add_timer(master, ospf_config_finish, NULL, + OSPF_PRE_CONFIG_MAX_WAIT_SECONDS, &t_ospf_cfg); +} + +static void ospf_config_end(void) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("ospfd config end callback received."); + + EVENT_OFF(t_ospf_cfg); +} + +/* OSPFd main routine. */ +int main(int argc, char **argv) +{ +#ifdef SUPPORT_OSPF_API + /* OSPF apiserver is disabled by default. */ + ospf_apiserver_enable = 0; +#endif /* SUPPORT_OSPF_API */ + + frr_preinit(&ospfd_di, argc, argv); + frr_opt_add("n:a", longopts, + " -n, --instance Set the instance id\n" + " -a, --apiserver Enable OSPF apiserver\n"); + + while (1) { + int opt; + + opt = frr_getopt(argc, argv, NULL); + + if (opt == EOF) + break; + + switch (opt) { + case 'n': + ospfd_di.instance = ospf_instance = atoi(optarg); + if (ospf_instance < 1) + exit(0); + break; + case 0: + break; +#ifdef SUPPORT_OSPF_API + case 'a': + ospf_apiserver_enable = 1; + break; +#endif /* SUPPORT_OSPF_API */ + default: + frr_help_exit(1); + } + } + + /* Invoked by a priviledged user? -- endo. */ + if (geteuid() != 0) { + errno = EPERM; + perror(ospfd_di.progname); + exit(1); + } + + /* OSPF master init. */ + ospf_master_init(frr_init()); + + /* Initializations. */ + master = om->master; + + /* Library inits. */ + ospf_debug_init(); + ospf_vrf_init(); + + access_list_init(); + prefix_list_init(); + keychain_init(); + + /* Configuration processing callback initialization. */ + cmd_init_config_callbacks(ospf_config_start, ospf_config_end); + + /* OSPFd inits. */ + ospf_if_init(); + ospf_zebra_init(master, ospf_instance); + + /* OSPF vty inits. */ + ospf_vty_init(); + ospf_vty_show_init(); + ospf_vty_clear_init(); + + /* OSPF BFD init */ + ospf_bfd_init(master); + + /* OSPF LDP IGP Sync init */ + ospf_ldp_sync_init(); + + ospf_route_map_init(); + ospf_opaque_init(); + ospf_gr_init(); + ospf_gr_helper_init(); + + /* OSPF errors init */ + ospf_error_init(); + + frr_config_fork(); + frr_run(master); + + /* Not reached. */ + return 0; +} diff --git a/ospfd/ospf_memory.c b/ospfd/ospf_memory.c new file mode 100644 index 0000000..9854c8c --- /dev/null +++ b/ospfd/ospf_memory.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* ospfd memory type definitions + * + * Copyright (C) 2015 David Lamparter + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ospf_memory.h" + +DEFINE_MGROUP(OSPFD, "ospfd"); +DEFINE_MTYPE(OSPFD, OSPF_TOP, "OSPF top"); +DEFINE_MTYPE(OSPFD, OSPF_AREA, "OSPF area"); +DEFINE_MTYPE(OSPFD, OSPF_AREA_RANGE, "OSPF area range"); +DEFINE_MTYPE(OSPFD, OSPF_NETWORK, "OSPF network"); +DEFINE_MTYPE(OSPFD, OSPF_NEIGHBOR_STATIC, "OSPF static nbr"); +DEFINE_MTYPE(OSPFD, OSPF_IF, "OSPF interface"); +DEFINE_MTYPE(OSPFD, OSPF_NEIGHBOR, "OSPF neighbor"); +DEFINE_MTYPE(OSPFD, OSPF_ROUTE, "OSPF route"); +DEFINE_MTYPE(OSPFD, OSPF_TMP, "OSPF tmp mem"); +DEFINE_MTYPE(OSPFD, OSPF_LSA, "OSPF LSA"); +DEFINE_MTYPE(OSPFD, OSPF_LSA_DATA, "OSPF LSA data"); +DEFINE_MTYPE(OSPFD, OSPF_LSDB, "OSPF LSDB"); +DEFINE_MTYPE(OSPFD, OSPF_PACKET, "OSPF packet"); +DEFINE_MTYPE(OSPFD, OSPF_FIFO, "OSPF FIFO queue"); +DEFINE_MTYPE(OSPFD, OSPF_VERTEX, "OSPF vertex"); +DEFINE_MTYPE(OSPFD, OSPF_VERTEX_PARENT, "OSPF vertex parent"); +DEFINE_MTYPE(OSPFD, OSPF_NEXTHOP, "OSPF nexthop"); +DEFINE_MTYPE(OSPFD, OSPF_PATH, "OSPF path"); +DEFINE_MTYPE(OSPFD, OSPF_VL_DATA, "OSPF VL data"); +DEFINE_MTYPE(OSPFD, OSPF_CRYPT_KEY, "OSPF crypt key"); +DEFINE_MTYPE(OSPFD, OSPF_EXTERNAL_INFO, "OSPF ext. info"); +DEFINE_MTYPE(OSPFD, OSPF_DISTANCE, "OSPF distance"); +DEFINE_MTYPE(OSPFD, OSPF_IF_INFO, "OSPF if info"); +DEFINE_MTYPE(OSPFD, OSPF_IF_PARAMS, "OSPF if params"); +DEFINE_MTYPE(OSPFD, OSPF_MESSAGE, "OSPF message"); +DEFINE_MTYPE(OSPFD, OSPF_MPLS_TE, "OSPF MPLS parameters"); +DEFINE_MTYPE(OSPFD, OSPF_ROUTER_INFO, "OSPF Router Info parameters"); +DEFINE_MTYPE(OSPFD, OSPF_PCE_PARAMS, "OSPF PCE parameters"); +DEFINE_MTYPE(OSPFD, OSPF_EXT_PARAMS, "OSPF Extended parameters"); +DEFINE_MTYPE(OSPFD, OSPF_SR_PARAMS, "OSPF Segment Routing parameters"); +DEFINE_MTYPE(OSPFD, OSPF_GR_HELPER, "OSPF Graceful Restart Helper"); +DEFINE_MTYPE(OSPFD, OSPF_EXTERNAL_RT_AGGR, "OSPF External Route Summarisation"); +DEFINE_MTYPE(OSPFD, OSPF_P_SPACE, "OSPF TI-LFA P-Space"); +DEFINE_MTYPE(OSPFD, OSPF_Q_SPACE, "OSPF TI-LFA Q-Space"); diff --git a/ospfd/ospf_memory.h b/ospfd/ospf_memory.h new file mode 100644 index 0000000..d11b69a --- /dev/null +++ b/ospfd/ospf_memory.h @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* ospfd memory type declarations + * + * Copyright (C) 2015 David Lamparter + */ + +#ifndef _QUAGGA_OSPF_MEMORY_H +#define _QUAGGA_OSPF_MEMORY_H + +#include "memory.h" + +DECLARE_MGROUP(OSPFD); +DECLARE_MTYPE(OSPF_TOP); +DECLARE_MTYPE(OSPF_AREA); +DECLARE_MTYPE(OSPF_AREA_RANGE); +DECLARE_MTYPE(OSPF_NETWORK); +DECLARE_MTYPE(OSPF_NEIGHBOR_STATIC); +DECLARE_MTYPE(OSPF_IF); +DECLARE_MTYPE(OSPF_NEIGHBOR); +DECLARE_MTYPE(OSPF_ROUTE); +DECLARE_MTYPE(OSPF_TMP); +DECLARE_MTYPE(OSPF_LSA); +DECLARE_MTYPE(OSPF_LSA_DATA); +DECLARE_MTYPE(OSPF_LSDB); +DECLARE_MTYPE(OSPF_PACKET); +DECLARE_MTYPE(OSPF_FIFO); +DECLARE_MTYPE(OSPF_VERTEX); +DECLARE_MTYPE(OSPF_VERTEX_PARENT); +DECLARE_MTYPE(OSPF_NEXTHOP); +DECLARE_MTYPE(OSPF_PATH); +DECLARE_MTYPE(OSPF_VL_DATA); +DECLARE_MTYPE(OSPF_CRYPT_KEY); +DECLARE_MTYPE(OSPF_EXTERNAL_INFO); +DECLARE_MTYPE(OSPF_DISTANCE); +DECLARE_MTYPE(OSPF_IF_INFO); +DECLARE_MTYPE(OSPF_IF_PARAMS); +DECLARE_MTYPE(OSPF_MESSAGE); +DECLARE_MTYPE(OSPF_MPLS_TE); +DECLARE_MTYPE(OSPF_ROUTER_INFO); +DECLARE_MTYPE(OSPF_PCE_PARAMS); +DECLARE_MTYPE(OSPF_SR_PARAMS); +DECLARE_MTYPE(OSPF_EXT_PARAMS); +DECLARE_MTYPE(OSPF_GR_HELPER); +DECLARE_MTYPE(OSPF_EXTERNAL_RT_AGGR); +DECLARE_MTYPE(OSPF_P_SPACE); +DECLARE_MTYPE(OSPF_Q_SPACE); + +#endif /* _QUAGGA_OSPF_MEMORY_H */ diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c new file mode 100644 index 0000000..c238f05 --- /dev/null +++ b/ospfd/ospf_neighbor.c @@ -0,0 +1,499 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF Neighbor functions. + * Copyright (C) 1999, 2000 Toshiaki Takada + */ + +#include + +#include "lib/bfd.h" +#include "linklist.h" +#include "prefix.h" +#include "memory.h" +#include "command.h" +#include "frrevent.h" +#include "stream.h" +#include "table.h" +#include "log.h" +#include "json.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_network.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_bfd.h" +#include "ospfd/ospf_gr.h" + +/* Fill in the the 'key' as appropriate to retrieve the entry for nbr + * from the ospf_interface's nbrs table. Indexed by interface address + * for all cases except Virtual-link and PointToPoint interfaces, where + * neighbours are indexed by router-ID instead. + */ +static void ospf_nbr_key(struct ospf_interface *oi, struct ospf_neighbor *nbr, + struct prefix *key) +{ + key->family = AF_INET; + key->prefixlen = IPV4_MAX_BITLEN; + + /* vlinks are indexed by router-id */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK + || oi->type == OSPF_IFTYPE_POINTOPOINT) + key->u.prefix4 = nbr->router_id; + else + key->u.prefix4 = nbr->src; + return; +} + +struct ospf_neighbor *ospf_nbr_new(struct ospf_interface *oi) +{ + struct ospf_neighbor *nbr; + + /* Allcate new neighbor. */ + nbr = XCALLOC(MTYPE_OSPF_NEIGHBOR, sizeof(struct ospf_neighbor)); + + /* Relate neighbor to the interface. */ + nbr->oi = oi; + + /* Set default values. */ + nbr->state = NSM_Down; + + /* Set inheritance values. */ + nbr->v_inactivity = OSPF_IF_PARAM(oi, v_wait); + nbr->v_db_desc = OSPF_IF_PARAM(oi, retransmit_interval); + nbr->v_ls_req = OSPF_IF_PARAM(oi, retransmit_interval); + nbr->v_ls_upd = OSPF_IF_PARAM(oi, retransmit_interval); + nbr->priority = -1; + + /* DD flags. */ + nbr->dd_flags = OSPF_DD_FLAG_MS | OSPF_DD_FLAG_M | OSPF_DD_FLAG_I; + + /* Last received and sent DD. */ + nbr->last_send = NULL; + + nbr->nbr_nbma = NULL; + + ospf_lsdb_init(&nbr->db_sum); + ospf_lsdb_init(&nbr->ls_rxmt); + ospf_lsdb_init(&nbr->ls_req); + + nbr->crypt_seqnum = 0; + + /* Initialize GR Helper info*/ + nbr->gr_helper_info.recvd_grace_period = 0; + nbr->gr_helper_info.actual_grace_period = 0; + nbr->gr_helper_info.gr_helper_status = OSPF_GR_NOT_HELPER; + nbr->gr_helper_info.helper_exit_reason = OSPF_GR_HELPER_EXIT_NONE; + nbr->gr_helper_info.gr_restart_reason = OSPF_GR_UNKNOWN_RESTART; + + return nbr; +} + +void ospf_nbr_free(struct ospf_neighbor *nbr) +{ + /* Free DB summary list. */ + if (ospf_db_summary_count(nbr)) + ospf_db_summary_clear(nbr); + /* ospf_db_summary_delete_all (nbr); */ + + /* Free ls request list. */ + if (ospf_ls_request_count(nbr)) + ospf_ls_request_delete_all(nbr); + + /* Free retransmit list. */ + if (ospf_ls_retransmit_count(nbr)) + ospf_ls_retransmit_clear(nbr); + + /* Cleanup LSDBs. */ + ospf_lsdb_cleanup(&nbr->db_sum); + ospf_lsdb_cleanup(&nbr->ls_req); + ospf_lsdb_cleanup(&nbr->ls_rxmt); + + /* Clear last send packet. */ + if (nbr->last_send) + ospf_packet_free(nbr->last_send); + + if (nbr->nbr_nbma) { + nbr->nbr_nbma->nbr = NULL; + nbr->nbr_nbma = NULL; + } + + /* Cancel all timers. */ + EVENT_OFF(nbr->t_inactivity); + EVENT_OFF(nbr->t_db_desc); + EVENT_OFF(nbr->t_ls_req); + EVENT_OFF(nbr->t_ls_upd); + + /* Cancel all events. */ /* Thread lookup cost would be negligible. */ + event_cancel_event(master, nbr); + + bfd_sess_free(&nbr->bfd_session); + + EVENT_OFF(nbr->gr_helper_info.t_grace_timer); + + nbr->oi = NULL; + XFREE(MTYPE_OSPF_NEIGHBOR, nbr); +} + +/* Delete specified OSPF neighbor from interface. */ +void ospf_nbr_delete(struct ospf_neighbor *nbr) +{ + struct ospf_interface *oi; + struct route_node *rn; + struct prefix p; + + oi = nbr->oi; + + /* get appropriate prefix 'key' */ + ospf_nbr_key(oi, nbr, &p); + + rn = route_node_lookup(oi->nbrs, &p); + if (rn) { + /* If lookup for a NBR succeeds, the leaf route_node could + * only exist because there is (or was) a nbr there. + * If the nbr was deleted, the leaf route_node should have + * lost its last refcount too, and be deleted. + * Therefore a looked-up leaf route_node in nbrs table + * should never have NULL info. + */ + assert(rn->info); + + if (rn->info) { + rn->info = NULL; + route_unlock_node(rn); + } else + zlog_info("Can't find neighbor %pI4 in the interface %s", + &nbr->src, IF_NAME(oi)); + + route_unlock_node(rn); + } else { + /* + * This neighbor was not found, but before we move on and + * free the neighbor structre, make sure that it was not + * indexed incorrectly and ended up in the "worng" place + */ + + /* Reverse the lookup rules */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK + || oi->type == OSPF_IFTYPE_POINTOPOINT) + p.u.prefix4 = nbr->src; + else + p.u.prefix4 = nbr->router_id; + + rn = route_node_lookup(oi->nbrs, &p); + if (rn) { + /* We found the neighbor! + * Now make sure it is not the exact same neighbor + * structure that we are about to free + */ + if (nbr == rn->info) { + /* Same neighbor, drop the reference to it */ + rn->info = NULL; + route_unlock_node(rn); + } + route_unlock_node(rn); + } + } + + /* Free ospf_neighbor structure. */ + ospf_nbr_free(nbr); +} + +/* Check myself is in the neighbor list. */ +int ospf_nbr_bidirectional(struct in_addr *router_id, struct in_addr *neighbors, + int size) +{ + int i; + int max; + + max = size / sizeof(struct in_addr); + + for (i = 0; i < max; i++) + if (IPV4_ADDR_SAME(router_id, &neighbors[i])) + return 1; + + return 0; +} + +/* reset nbr_self */ +void ospf_nbr_self_reset(struct ospf_interface *oi, struct in_addr router_id) +{ + if (oi->nbr_self) + ospf_nbr_delete(oi->nbr_self); + + oi->nbr_self = ospf_nbr_new(oi); + ospf_nbr_add_self(oi, router_id); +} + +/* Add self to nbr list. */ +void ospf_nbr_add_self(struct ospf_interface *oi, struct in_addr router_id) +{ + struct prefix p; + struct route_node *rn; + + if (!oi->nbr_self) + oi->nbr_self = ospf_nbr_new(oi); + + /* Initial state */ + oi->nbr_self->address = *oi->address; + oi->nbr_self->priority = OSPF_IF_PARAM(oi, priority); + oi->nbr_self->router_id = router_id; + oi->nbr_self->src = oi->address->u.prefix4; + oi->nbr_self->state = NSM_TwoWay; + + switch (oi->area->external_routing) { + case OSPF_AREA_DEFAULT: + SET_FLAG(oi->nbr_self->options, OSPF_OPTION_E); + break; + case OSPF_AREA_STUB: + UNSET_FLAG(oi->nbr_self->options, OSPF_OPTION_E); + break; + case OSPF_AREA_NSSA: + UNSET_FLAG(oi->nbr_self->options, OSPF_OPTION_E); + SET_FLAG(oi->nbr_self->options, OSPF_OPTION_NP); + break; + } + + /* Add nbr_self to nbrs table */ + ospf_nbr_key(oi, oi->nbr_self, &p); + + rn = route_node_get(oi->nbrs, &p); + if (rn->info) { + /* There is already pseudo neighbor. */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "router_id %pI4 already present in neighbor table. node refcount %u", + &router_id, route_node_get_lock_count(rn)); + route_unlock_node(rn); + } else + rn->info = oi->nbr_self; +} + +/* Get neighbor count by status. + Specify status = 0, get all neighbor other than myself. */ +int ospf_nbr_count(struct ospf_interface *oi, int state) +{ + struct ospf_neighbor *nbr; + struct route_node *rn; + int count = 0; + + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) + if ((nbr = rn->info)) + if (!IPV4_ADDR_SAME(&nbr->router_id, + &oi->ospf->router_id)) + if (state == 0 || nbr->state == state) + count++; + + return count; +} + +int ospf_nbr_count_opaque_capable(struct ospf_interface *oi) +{ + struct ospf_neighbor *nbr; + struct route_node *rn; + int count = 0; + + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) + if ((nbr = rn->info)) + if (!IPV4_ADDR_SAME(&nbr->router_id, + &oi->ospf->router_id)) + if (nbr->state == NSM_Full) + if (CHECK_FLAG(nbr->options, + OSPF_OPTION_O)) + count++; + + return count; +} + +/* lookup nbr by address - use this only if you know you must + * otherwise use the ospf_nbr_lookup() wrapper, which deals + * with virtual link and PointToPoint neighbours + */ +struct ospf_neighbor *ospf_nbr_lookup_by_addr(struct route_table *nbrs, + struct in_addr *addr) +{ + struct prefix p; + struct route_node *rn; + struct ospf_neighbor *nbr; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = *addr; + + rn = route_node_lookup(nbrs, &p); + if (!rn) + return NULL; + + /* See comment in ospf_nbr_delete */ + assert(rn->info); + + if (rn->info == NULL) { + route_unlock_node(rn); + return NULL; + } + + nbr = (struct ospf_neighbor *)rn->info; + route_unlock_node(rn); + + return nbr; +} + +struct ospf_neighbor *ospf_nbr_lookup_by_routerid(struct route_table *nbrs, + struct in_addr *id) +{ + struct route_node *rn; + struct ospf_neighbor *nbr; + + for (rn = route_top(nbrs); rn; rn = route_next(rn)) + if ((nbr = rn->info) != NULL) + if (IPV4_ADDR_SAME(&nbr->router_id, id)) { + route_unlock_node(rn); + return nbr; + } + + return NULL; +} + +void ospf_renegotiate_optional_capabilities(struct ospf *top) +{ + struct listnode *node; + struct ospf_interface *oi; + struct route_table *nbrs; + struct route_node *rn; + struct ospf_neighbor *nbr; + uint8_t shutdown_save = top->inst_shutdown; + + /* At first, flush self-originated LSAs from routing domain. */ + ospf_flush_self_originated_lsas_now(top); + + /* ospf_flush_self_originated_lsas_now is primarily intended for shut + * down scenarios. Reset the inst_shutdown flag that it sets. We are + * just changing configuration, and the flag can change the scheduling + * of when maxage LSAs are sent. */ + top->inst_shutdown = shutdown_save; + + /* Revert all neighbor status to ExStart. */ + for (ALL_LIST_ELEMENTS_RO(top->oiflist, node, oi)) { + if ((nbrs = oi->nbrs) == NULL) + continue; + + for (rn = route_top(nbrs); rn; rn = route_next(rn)) { + if ((nbr = rn->info) == NULL || nbr == oi->nbr_self) + continue; + + if (nbr->state < NSM_ExStart) + continue; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Renegotiate optional capabilities with neighbor(%pI4)", + &nbr->router_id); + + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_SeqNumberMismatch); + } + } + + /* Refresh/Re-originate external LSAs (Type-7 and Type-5).*/ + ospf_external_lsa_rid_change(top); + + return; +} + + +struct ospf_neighbor *ospf_nbr_lookup(struct ospf_interface *oi, struct ip *iph, + struct ospf_header *ospfh) +{ + struct in_addr srcaddr = iph->ip_src; + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK + || oi->type == OSPF_IFTYPE_POINTOPOINT) + return (ospf_nbr_lookup_by_routerid(oi->nbrs, + &ospfh->router_id)); + else + return (ospf_nbr_lookup_by_addr(oi->nbrs, &srcaddr)); +} + +static struct ospf_neighbor *ospf_nbr_add(struct ospf_interface *oi, + struct ospf_header *ospfh, + struct prefix *p) +{ + struct ospf_neighbor *nbr; + + nbr = ospf_nbr_new(oi); + nbr->state = NSM_Down; + nbr->src = p->u.prefix4; + memcpy(&nbr->address, p, sizeof(struct prefix)); + + nbr->nbr_nbma = NULL; + if (oi->type == OSPF_IFTYPE_NBMA) { + struct ospf_nbr_nbma *nbr_nbma; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(oi->nbr_nbma, node, nbr_nbma)) { + if (IPV4_ADDR_SAME(&nbr_nbma->addr, &nbr->src)) { + nbr_nbma->nbr = nbr; + nbr->nbr_nbma = nbr_nbma; + + if (nbr_nbma->t_poll) + EVENT_OFF(nbr_nbma->t_poll); + + nbr->state_change = nbr_nbma->state_change + 1; + } + } + } + + /* New nbr, save the crypto sequence number if necessary */ + if (ntohs(ospfh->auth_type) == OSPF_AUTH_CRYPTOGRAPHIC) + nbr->crypt_seqnum = ospfh->u.crypt.crypt_seqnum; + + /* Configure BFD if interface has it. */ + ospf_neighbor_bfd_apply(nbr); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("NSM[%s:%pI4]: start", IF_NAME(oi), + &nbr->router_id); + + return nbr; +} + +struct ospf_neighbor *ospf_nbr_get(struct ospf_interface *oi, + struct ospf_header *ospfh, struct ip *iph, + struct prefix *p) +{ + struct route_node *rn; + struct prefix key; + struct ospf_neighbor *nbr; + + key.family = AF_INET; + key.prefixlen = IPV4_MAX_BITLEN; + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK + || oi->type == OSPF_IFTYPE_POINTOPOINT) + key.u.prefix4 = ospfh->router_id; /* index vlink and ptp nbrs by + router-id */ + else + key.u.prefix4 = iph->ip_src; + + rn = route_node_get(oi->nbrs, &key); + if (rn->info) { + route_unlock_node(rn); + nbr = rn->info; + + if (oi->type == OSPF_IFTYPE_NBMA && nbr->state == NSM_Attempt) { + nbr->src = iph->ip_src; + memcpy(&nbr->address, p, sizeof(struct prefix)); + } + } else { + rn->info = nbr = ospf_nbr_add(oi, ospfh, p); + } + + nbr->router_id = ospfh->router_id; + + return nbr; +} diff --git a/ospfd/ospf_neighbor.h b/ospfd/ospf_neighbor.h new file mode 100644 index 0000000..07d095f --- /dev/null +++ b/ospfd/ospf_neighbor.h @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF Neighbor functions. + * Copyright (C) 1999, 2000 Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPF_NEIGHBOR_H +#define _ZEBRA_OSPF_NEIGHBOR_H + +#include +#include + +/* Neighbor Data Structure */ +struct ospf_neighbor { + /* This neighbor's parent ospf interface. */ + struct ospf_interface *oi; + + /* OSPF neighbor Information */ + uint8_t state; /* NSM status. */ + uint8_t dd_flags; /* DD bit flags. */ + uint32_t dd_seqnum; /* DD Sequence Number. */ + + /* Neighbor Information from Hello. */ + struct prefix address; /* Neighbor Interface Address. */ + + struct in_addr src; /* Src address. */ + struct in_addr router_id; /* Router ID. */ + uint8_t options; /* Options. */ + int priority; /* Router Priority. */ + struct in_addr d_router; /* Designated Router. */ + struct in_addr bd_router; /* Backup Designated Router. */ + + /* Last sent Database Description packet. */ + struct ospf_packet *last_send; + /* Timestemp when last Database Description packet was sent */ + struct timeval last_send_ts; + + /* Last received Databse Description packet. */ + struct { + uint8_t options; + uint8_t flags; + uint32_t dd_seqnum; + } last_recv; + + /* LSA data. */ + struct ospf_lsdb ls_rxmt; + struct ospf_lsdb db_sum; + struct ospf_lsdb ls_req; + struct ospf_lsa *ls_req_last; + + uint32_t crypt_seqnum; /* Cryptographic Sequence Number. */ + + /* Timer values. */ + uint32_t v_inactivity; + uint32_t v_db_desc; + uint32_t v_ls_req; + uint32_t v_ls_upd; + + /* Threads. */ + struct event *t_inactivity; + struct event *t_db_desc; + struct event *t_ls_req; + struct event *t_ls_upd; + struct event *t_hello_reply; + + /* NBMA configured neighbour */ + struct ospf_nbr_nbma *nbr_nbma; + + /* Statistics */ + struct timeval ts_last_progress; /* last advance of NSM */ + struct timeval ts_last_regress; /* last regressive NSM change */ + const char *last_regress_str; /* Event which last regressed NSM */ + uint32_t state_change; /* NSM state change counter */ + + /* BFD information */ + struct bfd_session_params *bfd_session; + + /* ospf graceful restart HELPER info */ + struct ospf_helper_info gr_helper_info; +}; + +/* Macros. */ +#define NBR_IS_DR(n) IPV4_ADDR_SAME (&n->address.u.prefix4, &n->d_router) +#define NBR_IS_BDR(n) IPV4_ADDR_SAME (&n->address.u.prefix4, &n->bd_router) + +/* Prototypes. */ +extern struct ospf_neighbor *ospf_nbr_new(struct ospf_interface *); +extern void ospf_nbr_free(struct ospf_neighbor *); +extern void ospf_nbr_delete(struct ospf_neighbor *); +extern int ospf_nbr_bidirectional(struct in_addr *, struct in_addr *, int); +extern void ospf_nbr_self_reset(struct ospf_interface *, struct in_addr); +extern void ospf_nbr_add_self(struct ospf_interface *, struct in_addr); +extern int ospf_nbr_count(struct ospf_interface *, int); +extern int ospf_nbr_count_opaque_capable(struct ospf_interface *); +extern struct ospf_neighbor *ospf_nbr_get(struct ospf_interface *, + struct ospf_header *, struct ip *, + struct prefix *); +extern struct ospf_neighbor *ospf_nbr_lookup(struct ospf_interface *, + struct ip *, struct ospf_header *); +extern struct ospf_neighbor *ospf_nbr_lookup_by_addr(struct route_table *, + struct in_addr *); +extern struct ospf_neighbor *ospf_nbr_lookup_by_routerid(struct route_table *, + struct in_addr *); +extern void ospf_renegotiate_optional_capabilities(struct ospf *top); +#endif /* _ZEBRA_OSPF_NEIGHBOR_H */ diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c new file mode 100644 index 0000000..801f75a --- /dev/null +++ b/ospfd/ospf_network.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF network related functions + * Copyright (C) 1999 Toshiaki Takada + */ + +#include + +#include "frrevent.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "sockunion.h" +#include "log.h" +#include "sockopt.h" +#include "privs.h" +#include "lib_errors.h" +#include "lib/table.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_network.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_dump.h" + +/* Join to the OSPF ALL SPF ROUTERS multicast group. */ +int ospf_if_add_allspfrouters(struct ospf *top, struct prefix *p, + ifindex_t ifindex) +{ + int ret; + + ret = setsockopt_ipv4_multicast(top->fd, IP_ADD_MEMBERSHIP, + p->u.prefix4, htonl(OSPF_ALLSPFROUTERS), + ifindex); + if (ret < 0) + flog_err( + EC_LIB_SOCKET, + "can't setsockopt IP_ADD_MEMBERSHIP (fd %d, addr %pI4, ifindex %u, AllSPFRouters): %s; perhaps a kernel limit on # of multicast group memberships has been exceeded?", + top->fd, &p->u.prefix4, ifindex, + safe_strerror(errno)); + else { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "interface %pI4 [%u] join AllSPFRouters Multicast group.", + &p->u.prefix4, ifindex); + } + + return ret; +} + +int ospf_if_drop_allspfrouters(struct ospf *top, struct prefix *p, + ifindex_t ifindex) +{ + int ret; + + ret = setsockopt_ipv4_multicast(top->fd, IP_DROP_MEMBERSHIP, + p->u.prefix4, htonl(OSPF_ALLSPFROUTERS), + ifindex); + if (ret < 0) + flog_err(EC_LIB_SOCKET, + "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %pI4, ifindex %u, AllSPFRouters): %s", + top->fd, &p->u.prefix4, ifindex, + safe_strerror(errno)); + else { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "interface %pI4 [%u] leave AllSPFRouters Multicast group.", + &p->u.prefix4, ifindex); + } + + return ret; +} + +/* Join to the OSPF ALL Designated ROUTERS multicast group. */ +int ospf_if_add_alldrouters(struct ospf *top, struct prefix *p, + ifindex_t ifindex) +{ + int ret; + + ret = setsockopt_ipv4_multicast(top->fd, IP_ADD_MEMBERSHIP, + p->u.prefix4, htonl(OSPF_ALLDROUTERS), + ifindex); + if (ret < 0) + flog_err( + EC_LIB_SOCKET, + "can't setsockopt IP_ADD_MEMBERSHIP (fd %d, addr %pI4, ifindex %u, AllDRouters): %s; perhaps a kernel limit on # of multicast group memberships has been exceeded?", + top->fd, &p->u.prefix4, ifindex, + safe_strerror(errno)); + else { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "interface %pI4 [%u] join AllDRouters Multicast group.", + &p->u.prefix4, ifindex); + } + return ret; +} + +int ospf_if_drop_alldrouters(struct ospf *top, struct prefix *p, + ifindex_t ifindex) +{ + int ret; + + ret = setsockopt_ipv4_multicast(top->fd, IP_DROP_MEMBERSHIP, + p->u.prefix4, htonl(OSPF_ALLDROUTERS), + ifindex); + if (ret < 0) + flog_err(EC_LIB_SOCKET, + "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %pI4, ifindex %u, AllDRouters): %s", + top->fd, &p->u.prefix4, ifindex, + safe_strerror(errno)); + else if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "interface %pI4 [%u] leave AllDRouters Multicast group.", + &p->u.prefix4, ifindex); + + return ret; +} + +int ospf_if_ipmulticast(int fd, struct prefix *p, ifindex_t ifindex) +{ + uint8_t val; + int ret, len; + + /* Prevent receiving self-origined multicast packets. */ + ret = setsockopt_ipv4_multicast_loop(fd, 0); + if (ret < 0) + flog_err(EC_LIB_SOCKET, + "can't setsockopt IP_MULTICAST_LOOP(0) for fd %d: %s", + fd, safe_strerror(errno)); + + /* Explicitly set multicast ttl to 1 -- endo. */ + val = 1; + len = sizeof(val); + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&val, len); + if (ret < 0) + flog_err(EC_LIB_SOCKET, + "can't setsockopt IP_MULTICAST_TTL(1) for fd %d: %s", + fd, safe_strerror(errno)); +#ifndef GNU_LINUX + /* For GNU LINUX ospf_write uses IP_PKTINFO, in_pktinfo to send + * packet out of ifindex. Below would be used Non Linux system. + */ + ret = setsockopt_ipv4_multicast_if(fd, p->u.prefix4, ifindex); + if (ret < 0) + flog_err(EC_LIB_SOCKET, + "can't setsockopt IP_MULTICAST_IF(fd %d, addr %pI4, ifindex %u): %s", + fd, &p->u.prefix4, ifindex, + safe_strerror(errno)); +#endif + + return ret; +} + +/* + * Helper to open and set up a socket; returns the new fd on success, + * -1 on error. + */ +static int sock_init_common(vrf_id_t vrf_id, const char *name, int proto, + int *pfd) +{ + int ospf_sock; + int ret, hincl = 1; + + if (vrf_id == VRF_UNKNOWN) { + /* silently return since VRF is not ready */ + return -1; + } + + frr_with_privs(&ospfd_privs) { + ospf_sock = vrf_socket(AF_INET, SOCK_RAW, proto, vrf_id, name); + if (ospf_sock < 0) { + flog_err(EC_LIB_SOCKET, "%s: socket: %s", __func__, + safe_strerror(errno)); + return -1; + } + +#ifdef IP_HDRINCL + /* we will include IP header with packet */ + ret = setsockopt(ospf_sock, IPPROTO_IP, IP_HDRINCL, &hincl, + sizeof(hincl)); + if (ret < 0) { + flog_err(EC_LIB_SOCKET, + "Can't set IP_HDRINCL option for fd %d: %s", + ospf_sock, safe_strerror(errno)); + break; + } +#elif defined(IPTOS_PREC_INTERNETCONTROL) +#warning "IP_HDRINCL not available on this system" +#warning "using IPTOS_PREC_INTERNETCONTROL" + ret = setsockopt_ipv4_tos(ospf_sock, + IPTOS_PREC_INTERNETCONTROL); + if (ret < 0) { + flog_err(EC_LIB_SOCKET, + "can't set sockopt IP_TOS %d to socket %d: %s", + tos, ospf_sock, safe_strerror(errno)); + break; + } +#else /* !IPTOS_PREC_INTERNETCONTROL */ +#warning "IP_HDRINCL not available, nor is IPTOS_PREC_INTERNETCONTROL" + flog_err(EC_LIB_UNAVAILABLE, "IP_HDRINCL option not available"); +#endif /* IP_HDRINCL */ + + ret = setsockopt_ifindex(AF_INET, ospf_sock, 1); + + if (ret < 0) + flog_err(EC_LIB_SOCKET, + "Can't set pktinfo option for fd %d", + ospf_sock); + } + + *pfd = ospf_sock; + + return ret; +} + +/* + * Update a socket bufsize(s), based on its ospf instance + */ +void ospf_sock_bufsize_update(const struct ospf *ospf, int sock, + enum ospf_sock_type_e type) +{ + int bufsize; + + if (type == OSPF_SOCK_BOTH || type == OSPF_SOCK_RECV) { + bufsize = ospf->recv_sock_bufsize; + setsockopt_so_recvbuf(sock, bufsize); + } + + if (type == OSPF_SOCK_BOTH || type == OSPF_SOCK_SEND) { + bufsize = ospf->send_sock_bufsize; + setsockopt_so_sendbuf(sock, bufsize); + } +} + +int ospf_sock_init(struct ospf *ospf) +{ + int ret; + + /* silently ignore. already done */ + if (ospf->fd > 0) + return -1; + + ret = sock_init_common(ospf->vrf_id, ospf->name, IPPROTO_OSPFIGP, + &(ospf->fd)); + + if (ret >= 0) /* Update socket buffer sizes */ + ospf_sock_bufsize_update(ospf, ospf->fd, OSPF_SOCK_BOTH); + + return ret; +} + +/* + * Open per-interface write socket + */ +int ospf_ifp_sock_init(struct interface *ifp) +{ + struct ospf_if_info *oii; + struct ospf_interface *oi = NULL; + struct ospf *ospf = NULL; + struct route_node *rn; + int ret; + + oii = IF_OSPF_IF_INFO(ifp); + if (oii == NULL) + return -1; + + if (oii->oii_fd > 0) + return 0; + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + if (rn && rn->info) { + oi = rn->info; + ospf = oi->ospf; + break; + } + } + + if (ospf == NULL) + return -1; + + ret = sock_init_common(ifp->vrf->vrf_id, ifp->name, IPPROTO_OSPFIGP, + &oii->oii_fd); + + if (ret >= 0) { /* Update socket buffer sizes */ + /* Write-only, so no recv buf */ + setsockopt_so_recvbuf(oii->oii_fd, 0); + + ospf_sock_bufsize_update(ospf, oii->oii_fd, OSPF_SOCK_SEND); + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: ifp %s, oii %p, fd %d", __func__, ifp->name, + oii, oii->oii_fd); + + return ret; +} + +/* + * Close per-interface write socket + */ +int ospf_ifp_sock_close(struct interface *ifp) +{ + struct ospf_if_info *oii; + + oii = IF_OSPF_IF_INFO(ifp); + if (oii == NULL) + return 0; + + if (oii->oii_fd > 0) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: ifp %s, oii %p, fd %d", __func__, + ifp->name, oii, oii->oii_fd); + + close(oii->oii_fd); + oii->oii_fd = -1; + } + + return 0; +} diff --git a/ospfd/ospf_network.h b/ospfd/ospf_network.h new file mode 100644 index 0000000..b810bad --- /dev/null +++ b/ospfd/ospf_network.h @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF network related functions. + * Copyright (C) 1999 Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPF_NETWORK_H +#define _ZEBRA_OSPF_NETWORK_H + +/* Prototypes. */ +extern int ospf_if_add_allspfrouters(struct ospf *, struct prefix *, ifindex_t); +extern int ospf_if_drop_allspfrouters(struct ospf *, struct prefix *, + ifindex_t); +extern int ospf_if_add_alldrouters(struct ospf *, struct prefix *, ifindex_t); +extern int ospf_if_drop_alldrouters(struct ospf *, struct prefix *, ifindex_t); +extern int ospf_if_ipmulticast(int fd, struct prefix *, ifindex_t); +extern int ospf_sock_init(struct ospf *ospf); +/* Open, close per-interface write socket */ +int ospf_ifp_sock_init(struct interface *ifp); +int ospf_ifp_sock_close(struct interface *ifp); + +enum ospf_sock_type_e { + OSPF_SOCK_NONE = 0, + OSPF_SOCK_RECV, + OSPF_SOCK_SEND, + OSPF_SOCK_BOTH +}; + +void ospf_sock_bufsize_update(const struct ospf *ospf, int sock, + enum ospf_sock_type_e type); + +#endif /* _ZEBRA_OSPF_NETWORK_H */ diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c new file mode 100644 index 0000000..bcbe028 --- /dev/null +++ b/ospfd/ospf_nsm.c @@ -0,0 +1,875 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF version 2 Neighbor State Machine + * From RFC2328 [OSPF Version 2] + * Copyright (C) 1999, 2000 Toshiaki Takada + */ + +#include + +#include "frrevent.h" +#include "memory.h" +#include "hash.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "stream.h" +#include "table.h" +#include "log.h" +#include "command.h" +#include "network.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_network.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_bfd.h" +#include "ospfd/ospf_gr.h" +#include "ospfd/ospf_errors.h" + +DEFINE_HOOK(ospf_nsm_change, + (struct ospf_neighbor * on, int state, int oldstate), + (on, state, oldstate)); + +static void nsm_clear_adj(struct ospf_neighbor *); + +/* OSPF NSM Timer functions. */ +static void ospf_inactivity_timer(struct event *thread) +{ + struct ospf_neighbor *nbr; + + nbr = EVENT_ARG(thread); + nbr->t_inactivity = NULL; + + if (IS_DEBUG_OSPF(nsm, NSM_TIMERS)) + zlog_debug("NSM[%s:%pI4:%s]: Timer (Inactivity timer expire)", + IF_NAME(nbr->oi), &nbr->router_id, + ospf_get_name(nbr->oi->ospf)); + + /* Dont trigger NSM_InactivityTimer event , if the current + * router acting as HELPER for this neighbour. + */ + if (!OSPF_GR_IS_ACTIVE_HELPER(nbr)) + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_InactivityTimer); + else { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, Acting as HELPER for this neighbour, So restart the dead timer", + __func__); + OSPF_NSM_TIMER_ON(nbr->t_inactivity, ospf_inactivity_timer, + nbr->v_inactivity); + } +} + +static void ospf_db_desc_timer(struct event *thread) +{ + struct ospf_neighbor *nbr; + + nbr = EVENT_ARG(thread); + nbr->t_db_desc = NULL; + + if (IS_DEBUG_OSPF(nsm, NSM_TIMERS)) + zlog_debug("NSM[%s:%pI4:%s]: Timer (DD Retransmit timer expire)", + IF_NAME(nbr->oi), &nbr->src, + ospf_get_name(nbr->oi->ospf)); + + /* resent last send DD packet. */ + assert(nbr->last_send); + ospf_db_desc_resend(nbr); + + /* DD Retransmit timer set. */ + OSPF_NSM_TIMER_ON(nbr->t_db_desc, ospf_db_desc_timer, nbr->v_db_desc); +} + +/* Hook function called after ospf NSM event is occurred. + * + * Set/clear any timers whose condition is implicit to the neighbour + * state. There may be other timers which are set/unset according to other + * state. + * + * We rely on this function to properly clear timers in lower states, + * particularly before deleting a neighbour. + */ +static void nsm_timer_set(struct ospf_neighbor *nbr) +{ + switch (nbr->state) { + case NSM_Deleted: + case NSM_Down: + EVENT_OFF(nbr->t_inactivity); + EVENT_OFF(nbr->t_hello_reply); + /* fallthru */ + case NSM_Attempt: + case NSM_Init: + case NSM_TwoWay: + EVENT_OFF(nbr->t_db_desc); + EVENT_OFF(nbr->t_ls_upd); + EVENT_OFF(nbr->t_ls_req); + break; + case NSM_ExStart: + OSPF_NSM_TIMER_ON(nbr->t_db_desc, ospf_db_desc_timer, + nbr->v_db_desc); + EVENT_OFF(nbr->t_ls_upd); + EVENT_OFF(nbr->t_ls_req); + break; + case NSM_Exchange: + OSPF_NSM_TIMER_ON(nbr->t_ls_upd, ospf_ls_upd_timer, + nbr->v_ls_upd); + if (!IS_SET_DD_MS(nbr->dd_flags)) + EVENT_OFF(nbr->t_db_desc); + break; + case NSM_Loading: + case NSM_Full: + default: + EVENT_OFF(nbr->t_db_desc); + break; + } +} + +/* 10.4 of RFC2328, indicate whether an adjacency is appropriate with + * the given neighbour + */ +int nsm_should_adj(struct ospf_neighbor *nbr) +{ + struct ospf_interface *oi = nbr->oi; + + /* These network types must always form adjacencies. */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT + || oi->type == OSPF_IFTYPE_POINTOMULTIPOINT + || oi->type == OSPF_IFTYPE_VIRTUALLINK + /* Router itself is the DRouter or the BDRouter. */ + || IPV4_ADDR_SAME(&oi->address->u.prefix4, &DR(oi)) + || IPV4_ADDR_SAME(&oi->address->u.prefix4, &BDR(oi)) + /* Neighboring Router is the DRouter or the BDRouter. */ + || IPV4_ADDR_SAME(&nbr->address.u.prefix4, &DR(oi)) + || IPV4_ADDR_SAME(&nbr->address.u.prefix4, &BDR(oi))) + return 1; + + return 0; +} + +/* OSPF NSM functions. */ +static int nsm_hello_received(struct ospf_neighbor *nbr) +{ + /* Start or Restart Inactivity Timer. */ + EVENT_OFF(nbr->t_inactivity); + + OSPF_NSM_TIMER_ON(nbr->t_inactivity, ospf_inactivity_timer, + nbr->v_inactivity); + + if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma) + EVENT_OFF(nbr->nbr_nbma->t_poll); + + /* Send proactive ARP requests */ + if (nbr->state < NSM_Exchange) + ospf_proactively_arp(nbr); + + return 0; +} + +static int nsm_start(struct ospf_neighbor *nbr) +{ + if (nbr->nbr_nbma) + EVENT_OFF(nbr->nbr_nbma->t_poll); + + EVENT_OFF(nbr->t_inactivity); + + OSPF_NSM_TIMER_ON(nbr->t_inactivity, ospf_inactivity_timer, + nbr->v_inactivity); + + /* Send proactive ARP requests */ + ospf_proactively_arp(nbr); + + return 0; +} + +static int nsm_twoway_received(struct ospf_neighbor *nbr) +{ + int adj = nsm_should_adj(nbr); + + /* Send proactive ARP requests */ + if (adj) + ospf_proactively_arp(nbr); + + return (adj ? NSM_ExStart : NSM_TwoWay); +} + +int ospf_db_summary_count(struct ospf_neighbor *nbr) +{ + return ospf_lsdb_count_all(&nbr->db_sum); +} + +int ospf_db_summary_isempty(struct ospf_neighbor *nbr) +{ + return ospf_lsdb_isempty(&nbr->db_sum); +} + +static int ospf_db_summary_add(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) +{ + switch (lsa->data->type) { + case OSPF_OPAQUE_LINK_LSA: + /* Exclude type-9 LSAs that does not have the same "oi" with + * "nbr". */ + if (ospf_if_exists(lsa->oi) != nbr->oi) + return 0; + break; + case OSPF_OPAQUE_AREA_LSA: + /* + * It is assured by the caller function "nsm_negotiation_done()" + * that every given LSA belongs to the same area with "nbr". + */ + break; + case OSPF_OPAQUE_AS_LSA: + default: + break; + } + + /* Stay away from any Local Translated Type-7 LSAs */ + if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT)) + return 0; + + if (IS_LSA_MAXAGE(lsa)) + ospf_ls_retransmit_add(nbr, lsa); + else + ospf_lsdb_add(&nbr->db_sum, lsa); + + return 0; +} + +void ospf_db_summary_clear(struct ospf_neighbor *nbr) +{ + struct ospf_lsdb *lsdb; + int i; + + lsdb = &nbr->db_sum; + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) { + struct route_table *table = lsdb->type[i].db; + struct route_node *rn; + + for (rn = route_top(table); rn; rn = route_next(rn)) + if (rn->info) + ospf_lsdb_delete(&nbr->db_sum, rn->info); + } +} + + +/* The area link state database consists of the router-LSAs, + network-LSAs and summary-LSAs contained in the area structure, + along with the AS-external-LSAs contained in the global structure. + AS-external-LSAs are omitted from a virtual neighbor's Database + summary list. AS-external-LSAs are omitted from the Database + summary list if the area has been configured as a stub. */ +static int nsm_negotiation_done(struct ospf_neighbor *nbr) +{ + struct ospf_area *area = nbr->oi->area; + struct ospf_lsa *lsa; + struct route_node *rn; + + /* Send proactive ARP requests */ + ospf_proactively_arp(nbr); + + LSDB_LOOP (ROUTER_LSDB(area), rn, lsa) + ospf_db_summary_add(nbr, lsa); + LSDB_LOOP (NETWORK_LSDB(area), rn, lsa) + ospf_db_summary_add(nbr, lsa); + LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa) + ospf_db_summary_add(nbr, lsa); + + /* Process only if the neighbor is opaque capable. */ + if (CHECK_FLAG(nbr->options, OSPF_OPTION_O)) { + LSDB_LOOP (OPAQUE_LINK_LSDB(area), rn, lsa) + ospf_db_summary_add(nbr, lsa); + LSDB_LOOP (OPAQUE_AREA_LSDB(area), rn, lsa) + ospf_db_summary_add(nbr, lsa); + } + + if (CHECK_FLAG(nbr->options, OSPF_OPTION_NP)) { + LSDB_LOOP (NSSA_LSDB(area), rn, lsa) + ospf_db_summary_add(nbr, lsa); + } + + /* For Stub/NSSA area, we should not send Type-4 and Type-5 LSAs */ + if (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK + && area->external_routing == OSPF_AREA_DEFAULT) { + LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa) + ospf_db_summary_add(nbr, lsa); + LSDB_LOOP (EXTERNAL_LSDB(nbr->oi->ospf), rn, lsa) + ospf_db_summary_add(nbr, lsa); + } + + if (CHECK_FLAG(nbr->options, OSPF_OPTION_O) + && (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK + && area->external_routing == OSPF_AREA_DEFAULT)) + LSDB_LOOP (OPAQUE_AS_LSDB(nbr->oi->ospf), rn, lsa) + ospf_db_summary_add(nbr, lsa); + + return 0; +} + +static int nsm_exchange_done(struct ospf_neighbor *nbr) +{ + if (ospf_ls_request_isempty(nbr)) + return NSM_Full; + + /* Send Link State Request. */ + if (nbr->t_ls_req == NULL) + ospf_ls_req_send(nbr); + + return NSM_Loading; +} + +static int nsm_adj_ok(struct ospf_neighbor *nbr) +{ + int next_state = nbr->state; + int adj = nsm_should_adj(nbr); + + if (nbr->state == NSM_TwoWay && adj == 1) { + next_state = NSM_ExStart; + + /* Send proactive ARP requests */ + ospf_proactively_arp(nbr); + } else if (nbr->state >= NSM_ExStart && adj == 0) + next_state = NSM_TwoWay; + + return next_state; +} + +/* Clear adjacency related state for a neighbour, intended where nbr + * transitions from > ExStart (i.e. a Full or forming adjacency) + * to <= ExStart. + */ +static void nsm_clear_adj(struct ospf_neighbor *nbr) +{ + /* Clear Database Summary list. */ + if (!ospf_db_summary_isempty(nbr)) + ospf_db_summary_clear(nbr); + + /* Clear Link State Request list. */ + if (!ospf_ls_request_isempty(nbr)) + ospf_ls_request_delete_all(nbr); + + /* Clear Link State Retransmission list. */ + if (!ospf_ls_retransmit_isempty(nbr)) + ospf_ls_retransmit_clear(nbr); + + if (CHECK_FLAG(nbr->options, OSPF_OPTION_O)) + UNSET_FLAG(nbr->options, OSPF_OPTION_O); +} + +static int nsm_kill_nbr(struct ospf_neighbor *nbr) +{ + struct ospf_interface *oi = nbr->oi; + struct ospf_neighbor *on; + struct route_node *rn; + + /* killing nbr_self is invalid */ + if (nbr == nbr->oi->nbr_self) { + assert(nbr != nbr->oi->nbr_self); + return 0; + } + + if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma != NULL) { + struct ospf_nbr_nbma *nbr_nbma = nbr->nbr_nbma; + + nbr_nbma->nbr = NULL; + nbr_nbma->state_change = nbr->state_change; + + nbr->nbr_nbma = NULL; + + OSPF_POLL_TIMER_ON(nbr_nbma->t_poll, ospf_poll_timer, + nbr_nbma->v_poll); + + if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) + zlog_debug( + "NSM[%s:%pI4:%s]: Down (PollIntervalTimer scheduled)", + IF_NAME(nbr->oi), + &nbr->address.u.prefix4, + ospf_get_name(nbr->oi->ospf)); + } + + /* + * Do we have any neighbors that are also operating + * on this interface? + */ + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + on = rn->info; + + if (!on) + continue; + + if (on == nbr || on == oi->nbr_self) + continue; + + /* + * on is in some state where we might be + * sending packets on this interface + */ + if (on->state > NSM_Down) { + route_unlock_node(rn); + return 0; + } + } + /* + * If we get here we know that this interface + * has no neighbors in a state where we could + * be sending packets. Let's flush anything + * we got. + */ + ospf_interface_fifo_flush(oi); + return 0; +} + +/* Neighbor State Machine */ +const struct { + int (*func)(struct ospf_neighbor *); + int next_state; +} NSM[OSPF_NSM_STATE_MAX][OSPF_NSM_EVENT_MAX] = { + { + /* DependUpon: dummy state. */ + {NULL, NSM_DependUpon}, /* NoEvent */ + {NULL, NSM_DependUpon}, /* HelloReceived */ + {NULL, NSM_DependUpon}, /* Start */ + {NULL, NSM_DependUpon}, /* 2-WayReceived */ + {NULL, NSM_DependUpon}, /* NegotiationDone */ + {NULL, NSM_DependUpon}, /* ExchangeDone */ + {NULL, NSM_DependUpon}, /* BadLSReq */ + {NULL, NSM_DependUpon}, /* LoadingDone */ + {NULL, NSM_DependUpon}, /* AdjOK? */ + {NULL, NSM_DependUpon}, /* SeqNumberMismatch */ + {NULL, NSM_DependUpon}, /* 1-WayReceived */ + {NULL, NSM_DependUpon}, /* KillNbr */ + {NULL, NSM_DependUpon}, /* InactivityTimer */ + {NULL, NSM_DependUpon}, /* LLDown */ + }, + { + /* Deleted: dummy state. */ + {NULL, NSM_Deleted}, /* NoEvent */ + {NULL, NSM_Deleted}, /* HelloReceived */ + {NULL, NSM_Deleted}, /* Start */ + {NULL, NSM_Deleted}, /* 2-WayReceived */ + {NULL, NSM_Deleted}, /* NegotiationDone */ + {NULL, NSM_Deleted}, /* ExchangeDone */ + {NULL, NSM_Deleted}, /* BadLSReq */ + {NULL, NSM_Deleted}, /* LoadingDone */ + {NULL, NSM_Deleted}, /* AdjOK? */ + {NULL, NSM_Deleted}, /* SeqNumberMismatch */ + {NULL, NSM_Deleted}, /* 1-WayReceived */ + {NULL, NSM_Deleted}, /* KillNbr */ + {NULL, NSM_Deleted}, /* InactivityTimer */ + {NULL, NSM_Deleted}, /* LLDown */ + }, + { + /* Down: */ + {NULL, NSM_DependUpon}, /* NoEvent */ + {nsm_hello_received, NSM_Init}, /* HelloReceived */ + {nsm_start, NSM_Attempt}, /* Start */ + {NULL, NSM_Down}, /* 2-WayReceived */ + {NULL, NSM_Down}, /* NegotiationDone */ + {NULL, NSM_Down}, /* ExchangeDone */ + {NULL, NSM_Down}, /* BadLSReq */ + {NULL, NSM_Down}, /* LoadingDone */ + {NULL, NSM_Down}, /* AdjOK? */ + {NULL, NSM_Down}, /* SeqNumberMismatch */ + {NULL, NSM_Down}, /* 1-WayReceived */ + {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */ + {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */ + {nsm_kill_nbr, NSM_Deleted}, /* LLDown */ + }, + { + /* Attempt: */ + {NULL, NSM_DependUpon}, /* NoEvent */ + {nsm_hello_received, NSM_Init}, /* HelloReceived */ + {NULL, NSM_Attempt}, /* Start */ + {NULL, NSM_Attempt}, /* 2-WayReceived */ + {NULL, NSM_Attempt}, /* NegotiationDone */ + {NULL, NSM_Attempt}, /* ExchangeDone */ + {NULL, NSM_Attempt}, /* BadLSReq */ + {NULL, NSM_Attempt}, /* LoadingDone */ + {NULL, NSM_Attempt}, /* AdjOK? */ + {NULL, NSM_Attempt}, /* SeqNumberMismatch */ + {NULL, NSM_Attempt}, /* 1-WayReceived */ + {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */ + {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */ + {nsm_kill_nbr, NSM_Deleted}, /* LLDown */ + }, + { + /* Init: */ + {NULL, NSM_DependUpon}, /* NoEvent */ + {nsm_hello_received, NSM_Init}, /* HelloReceived */ + {NULL, NSM_Init}, /* Start */ + {nsm_twoway_received, NSM_DependUpon}, /* 2-WayReceived */ + {NULL, NSM_Init}, /* NegotiationDone */ + {NULL, NSM_Init}, /* ExchangeDone */ + {NULL, NSM_Init}, /* BadLSReq */ + {NULL, NSM_Init}, /* LoadingDone */ + {NULL, NSM_Init}, /* AdjOK? */ + {NULL, NSM_Init}, /* SeqNumberMismatch */ + {NULL, NSM_Init}, /* 1-WayReceived */ + {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */ + {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */ + {nsm_kill_nbr, NSM_Deleted}, /* LLDown */ + }, + { + /* 2-Way: */ + {NULL, NSM_DependUpon}, /* NoEvent */ + {nsm_hello_received, NSM_TwoWay}, /* HelloReceived */ + {NULL, NSM_TwoWay}, /* Start */ + {NULL, NSM_TwoWay}, /* 2-WayReceived */ + {NULL, NSM_TwoWay}, /* NegotiationDone */ + {NULL, NSM_TwoWay}, /* ExchangeDone */ + {NULL, NSM_TwoWay}, /* BadLSReq */ + {NULL, NSM_TwoWay}, /* LoadingDone */ + {nsm_adj_ok, NSM_DependUpon}, /* AdjOK? */ + {NULL, NSM_TwoWay}, /* SeqNumberMismatch */ + {NULL, NSM_Init}, /* 1-WayReceived */ + {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */ + {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */ + {nsm_kill_nbr, NSM_Deleted}, /* LLDown */ + }, + { + /* ExStart: */ + {NULL, NSM_DependUpon}, /* NoEvent */ + {nsm_hello_received, NSM_ExStart}, /* HelloReceived */ + {NULL, NSM_ExStart}, /* Start */ + {NULL, NSM_ExStart}, /* 2-WayReceived */ + {nsm_negotiation_done, NSM_Exchange}, /* NegotiationDone */ + {NULL, NSM_ExStart}, /* ExchangeDone */ + {NULL, NSM_ExStart}, /* BadLSReq */ + {NULL, NSM_ExStart}, /* LoadingDone */ + {nsm_adj_ok, NSM_DependUpon}, /* AdjOK? */ + {NULL, NSM_ExStart}, /* SeqNumberMismatch */ + {NULL, NSM_Init}, /* 1-WayReceived */ + {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */ + {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */ + {nsm_kill_nbr, NSM_Deleted}, /* LLDown */ + }, + { + /* Exchange: */ + {NULL, NSM_DependUpon}, /* NoEvent */ + {nsm_hello_received, NSM_Exchange}, /* HelloReceived */ + {NULL, NSM_Exchange}, /* Start */ + {NULL, NSM_Exchange}, /* 2-WayReceived */ + {NULL, NSM_Exchange}, /* NegotiationDone */ + {nsm_exchange_done, NSM_DependUpon}, /* ExchangeDone */ + {NULL, NSM_ExStart}, /* BadLSReq */ + {NULL, NSM_Exchange}, /* LoadingDone */ + {nsm_adj_ok, NSM_DependUpon}, /* AdjOK? */ + {NULL, NSM_ExStart}, /* SeqNumberMismatch */ + {NULL, NSM_Init}, /* 1-WayReceived */ + {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */ + {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */ + {nsm_kill_nbr, NSM_Deleted}, /* LLDown */ + }, + { + /* Loading: */ + {NULL, NSM_DependUpon}, /* NoEvent */ + {nsm_hello_received, NSM_Loading}, /* HelloReceived */ + {NULL, NSM_Loading}, /* Start */ + {NULL, NSM_Loading}, /* 2-WayReceived */ + {NULL, NSM_Loading}, /* NegotiationDone */ + {NULL, NSM_Loading}, /* ExchangeDone */ + {NULL, NSM_ExStart}, /* BadLSReq */ + {NULL, NSM_Full}, /* LoadingDone */ + {nsm_adj_ok, NSM_DependUpon}, /* AdjOK? */ + {NULL, NSM_ExStart}, /* SeqNumberMismatch */ + {NULL, NSM_Init}, /* 1-WayReceived */ + {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */ + {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */ + {nsm_kill_nbr, NSM_Deleted}, /* LLDown */ + }, + { + /* Full: */ + {NULL, NSM_DependUpon}, /* NoEvent */ + {nsm_hello_received, NSM_Full}, /* HelloReceived */ + {NULL, NSM_Full}, /* Start */ + {NULL, NSM_Full}, /* 2-WayReceived */ + {NULL, NSM_Full}, /* NegotiationDone */ + {NULL, NSM_Full}, /* ExchangeDone */ + {NULL, NSM_ExStart}, /* BadLSReq */ + {NULL, NSM_Full}, /* LoadingDone */ + {nsm_adj_ok, NSM_DependUpon}, /* AdjOK? */ + {NULL, NSM_ExStart}, /* SeqNumberMismatch */ + {NULL, NSM_Init}, /* 1-WayReceived */ + {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */ + {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */ + {nsm_kill_nbr, NSM_Deleted}, /* LLDown */ + }, +}; + +static const char *const ospf_nsm_event_str[] = { + "NoEvent", "HelloReceived", "Start", + "2-WayReceived", "NegotiationDone", "ExchangeDone", + "BadLSReq", "LoadingDone", "AdjOK?", + "SeqNumberMismatch", "1-WayReceived", "KillNbr", + "InactivityTimer", "LLDown", +}; + +static void nsm_notice_state_change(struct ospf_neighbor *nbr, int next_state, + int event) +{ + /* Logging change of status. */ + if (IS_DEBUG_OSPF(nsm, NSM_STATUS)) + zlog_debug("NSM[%s:%pI4:%s]: State change %s -> %s (%s)", + IF_NAME(nbr->oi), &nbr->router_id, + ospf_get_name(nbr->oi->ospf), + lookup_msg(ospf_nsm_state_msg, nbr->state, NULL), + lookup_msg(ospf_nsm_state_msg, next_state, NULL), + ospf_nsm_event_str[event]); + + /* Optionally notify about adjacency changes */ + if (CHECK_FLAG(nbr->oi->ospf->config, OSPF_LOG_ADJACENCY_CHANGES) + && (CHECK_FLAG(nbr->oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL) + || (next_state == NSM_Full) || (next_state < nbr->state))) + zlog_notice( + "AdjChg: Nbr %pI4, NbrIP %pI4 (%s) on %s: %s -> %s (%s)", + &nbr->router_id, &nbr->src, + ospf_get_name(nbr->oi->ospf), IF_NAME(nbr->oi), + lookup_msg(ospf_nsm_state_msg, nbr->state, NULL), + lookup_msg(ospf_nsm_state_msg, next_state, NULL), + ospf_nsm_event_str[event]); + + /* Advance in NSM */ + if (next_state > nbr->state) + monotime(&nbr->ts_last_progress); + else /* regression in NSM */ + { + monotime(&nbr->ts_last_regress); + nbr->last_regress_str = ospf_nsm_event_str[event]; + } +} + +static void nsm_change_state(struct ospf_neighbor *nbr, int state) +{ + struct ospf_interface *oi = nbr->oi; + struct ospf_area *vl_area = NULL; + uint8_t old_state; + + /* Preserve old status. */ + old_state = nbr->state; + + /* Change to new status. */ + nbr->state = state; + + /* Statistics. */ + nbr->state_change++; + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + vl_area = ospf_area_lookup_by_area_id(oi->ospf, + oi->vl_data->vl_area_id); + + /* Generate NeighborChange ISM event. + * + * In response to NeighborChange, DR election is rerun. The information + * from the election process is required by the router-lsa construction. + * + * Therefore, trigger the event prior to refreshing the LSAs. */ + switch (oi->state) { + case ISM_DROther: + case ISM_Backup: + case ISM_DR: + if ((old_state < NSM_TwoWay && state >= NSM_TwoWay) + || (old_state >= NSM_TwoWay && state < NSM_TwoWay)) + OSPF_ISM_EVENT_EXECUTE(oi, ISM_NeighborChange); + break; + default: + /* ISM_PointToPoint -> ISM_Down, ISM_Loopback -> ISM_Down, etc. + */ + break; + } + + /* One of the neighboring routers changes to/from the FULL state. */ + if ((old_state != NSM_Full && state == NSM_Full) + || (old_state == NSM_Full && state != NSM_Full)) { + if (state == NSM_Full) { + oi->full_nbrs++; + oi->area->full_nbrs++; + + ospf_check_abr_status(oi->ospf); + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK && vl_area) + if (++vl_area->full_vls == 1) + ospf_schedule_abr_task(oi->ospf); + } else { + oi->full_nbrs--; + oi->area->full_nbrs--; + + ospf_check_abr_status(oi->ospf); + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK && vl_area) + if (vl_area->full_vls > 0) + if (--vl_area->full_vls == 0) + ospf_schedule_abr_task( + oi->ospf); + } + + if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) + zlog_info( + "%s:[%pI4:%s], %s -> %s): scheduling new router-LSA origination", + __func__, &nbr->router_id, + ospf_get_name(oi->ospf), + lookup_msg(ospf_nsm_state_msg, old_state, NULL), + lookup_msg(ospf_nsm_state_msg, state, NULL)); + + /* Dont originate router LSA if the current + * router is acting as a HELPER for this neighbour. + */ + if (!OSPF_GR_IS_ACTIVE_HELPER(nbr)) + ospf_router_lsa_update_area(oi->area); + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) { + vl_area = ospf_area_lookup_by_area_id( + oi->ospf, oi->vl_data->vl_area_id); + + if (vl_area) + ospf_router_lsa_update_area(vl_area); + } + + /* Dont originate/flush network LSA if the current + * router is acting as a HELPER for this neighbour. + */ + if (!OSPF_GR_IS_ACTIVE_HELPER(nbr)) { + /* Originate network-LSA. */ + if (oi->state == ISM_DR) { + if (oi->network_lsa_self + && oi->full_nbrs == 0) { + ospf_lsa_flush_area( + oi->network_lsa_self, oi->area); + ospf_lsa_unlock(&oi->network_lsa_self); + oi->network_lsa_self = NULL; + } else + ospf_network_lsa_update(oi); + } + } + + if (state == NSM_Full && oi->ospf->gr_info.restart_in_progress) + ospf_gr_check_adjs(oi->ospf); + } + + ospf_opaque_nsm_change(nbr, old_state); + + /* State changes from > ExStart to <= ExStart should clear any Exchange + * or Full/LSA Update related lists and state. + * Potential causal events: BadLSReq, SeqNumberMismatch, AdjOK? + */ + if ((old_state > NSM_ExStart) && (state <= NSM_ExStart)) + nsm_clear_adj(nbr); + + /* Start DD exchange protocol */ + if (state == NSM_ExStart) { + if (nbr->dd_seqnum == 0) + nbr->dd_seqnum = (uint32_t)frr_weak_random(); + else + nbr->dd_seqnum++; + + nbr->dd_flags = + OSPF_DD_FLAG_I | OSPF_DD_FLAG_M | OSPF_DD_FLAG_MS; + if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) + zlog_info( + "%s: Initializing [DD]: %pI4 with seqnum:%x , flags:%x", + ospf_get_name(oi->ospf), &nbr->router_id, + nbr->dd_seqnum, nbr->dd_flags); + ospf_db_desc_send(nbr); + } + + /* clear cryptographic sequence number */ + if (state == NSM_Down) + nbr->crypt_seqnum = 0; + + if (nbr->bfd_session) + ospf_bfd_trigger_event(nbr, old_state, state); + + /* Preserve old status? */ +} + +/* Execute NSM event process. */ +void ospf_nsm_event(struct event *thread) +{ + int event; + int next_state; + struct ospf_neighbor *nbr; + + nbr = EVENT_ARG(thread); + event = EVENT_VAL(thread); + + if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) + zlog_debug("NSM[%s:%pI4:%s]: %s (%s)", IF_NAME(nbr->oi), + &nbr->router_id, + ospf_get_name(nbr->oi->ospf), + lookup_msg(ospf_nsm_state_msg, nbr->state, NULL), + ospf_nsm_event_str[event]); + + next_state = NSM[nbr->state][event].next_state; + + /* Call function. */ + if (NSM[nbr->state][event].func != NULL) { + int func_state = (*(NSM[nbr->state][event].func))(nbr); + + if (NSM[nbr->state][event].next_state == NSM_DependUpon) + next_state = func_state; + else if (func_state) { + /* There's a mismatch between the FSM tables and what an + * FSM + * action/state-change function returned. State changes + * which + * do not have conditional/DependUpon next-states should + * not + * try set next_state. + */ + flog_err( + EC_OSPF_FSM_INVALID_STATE, + "NSM[%s:%pI4:%s]: %s (%s): Warning: action tried to change next_state to %s", + IF_NAME(nbr->oi), &nbr->router_id, + ospf_get_name(nbr->oi->ospf), + lookup_msg(ospf_nsm_state_msg, nbr->state, + NULL), + ospf_nsm_event_str[event], + lookup_msg(ospf_nsm_state_msg, func_state, + NULL)); + } + } + + assert(next_state != NSM_DependUpon); + + /* If state is changed. */ + if (next_state != nbr->state) { + int old_state = nbr->state; + + nsm_notice_state_change(nbr, next_state, event); + nsm_change_state(nbr, next_state); + + hook_call(ospf_nsm_change, nbr, next_state, old_state); + } + + /* Make sure timer is set. */ + nsm_timer_set(nbr); + + /* When event is NSM_KillNbr, InactivityTimer or LLDown, the neighbor + * is deleted. + * + * Rather than encode knowledge here of which events lead to NBR + * delete, we take our cue from the NSM table, via the dummy + * 'Deleted' neighbour state. + */ + if (nbr->state == NSM_Deleted) + ospf_nbr_delete(nbr); +} + +/* Check loading state. */ +void ospf_check_nbr_loading(struct ospf_neighbor *nbr) +{ + if (nbr->state == NSM_Loading) { + if (ospf_ls_request_isempty(nbr)) + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_LoadingDone); + else if (nbr->ls_req_last == NULL) + ospf_ls_req_event(nbr); + } +} diff --git a/ospfd/ospf_nsm.h b/ospfd/ospf_nsm.h new file mode 100644 index 0000000..82e1720 --- /dev/null +++ b/ospfd/ospf_nsm.h @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF version 2 Neighbor State Machine + * From RFC2328 [OSPF Version 2] + * Copyright (C) 1999 Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPF_NSM_H +#define _ZEBRA_OSPF_NSM_H + +#include "hook.h" + +/* OSPF Neighbor State Machine State. */ +#define NSM_DependUpon 0 +#define NSM_Deleted 1 +#define NSM_Down 2 +#define NSM_Attempt 3 +#define NSM_Init 4 +#define NSM_TwoWay 5 +#define NSM_ExStart 6 +#define NSM_Exchange 7 +#define NSM_Loading 8 +#define NSM_Full 9 +#define OSPF_NSM_STATE_MAX 10 + +/* OSPF Neighbor State Machine Event. */ +#define NSM_NoEvent 0 +#define NSM_HelloReceived 1 /* HelloReceived in the protocol */ +#define NSM_Start 2 +#define NSM_TwoWayReceived 3 +#define NSM_NegotiationDone 4 +#define NSM_ExchangeDone 5 +#define NSM_BadLSReq 6 +#define NSM_LoadingDone 7 +#define NSM_AdjOK 8 +#define NSM_SeqNumberMismatch 9 +#define NSM_OneWayReceived 10 +#define NSM_KillNbr 11 +#define NSM_InactivityTimer 12 +#define NSM_LLDown 13 +#define OSPF_NSM_EVENT_MAX 14 + +/* Macro for OSPF NSM timer turn on. */ +#define OSPF_NSM_TIMER_ON(T, F, V) event_add_timer(master, (F), nbr, (V), &(T)) + +/* Macro for OSPF NSM schedule event. */ +#define OSPF_NSM_EVENT_SCHEDULE(N, E) \ + event_add_event(master, ospf_nsm_event, (N), (E), NULL) + +/* Macro for OSPF NSM execute event. */ +#define OSPF_NSM_EVENT_EXECUTE(N, E) \ + event_execute(master, ospf_nsm_event, (N), (E), NULL) + +/* Prototypes. */ +extern void ospf_nsm_event(struct event *e); +extern void ospf_check_nbr_loading(struct ospf_neighbor *nbr); +extern int ospf_db_summary_isempty(struct ospf_neighbor *nbr); +extern int ospf_db_summary_count(struct ospf_neighbor *nbr); +extern void ospf_db_summary_clear(struct ospf_neighbor *nbr); +extern int nsm_should_adj(struct ospf_neighbor *nbr); +DECLARE_HOOK(ospf_nsm_change, + (struct ospf_neighbor * on, int state, int oldstate), + (on, state, oldstate)); + +#endif /* _ZEBRA_OSPF_NSM_H */ diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c new file mode 100644 index 0000000..27f47a6 --- /dev/null +++ b/ospfd/ospf_opaque.c @@ -0,0 +1,2166 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This is an implementation of rfc2370. + * Copyright (C) 2001 KDD R&D Laboratories, Inc. + * http://www.kddlabs.co.jp/ + */ + +#include + +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "memory.h" +#include "command.h" +#include "vty.h" +#include "stream.h" +#include "log.h" +#include "frrevent.h" +#include "hash.h" +#include "sockunion.h" /* for inet_aton() */ +#include "printfrr.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_te.h" +#include "ospfd/ospf_sr.h" +#include "ospfd/ospf_ri.h" +#include "ospfd/ospf_ext.h" +#include "ospfd/ospf_errors.h" + +DEFINE_MTYPE_STATIC(OSPFD, OSPF_OPAQUE_FUNCTAB, "OSPF opaque function table"); +DEFINE_MTYPE_STATIC(OSPFD, OPAQUE_INFO_PER_TYPE, "OSPF opaque per-type info"); +DEFINE_MTYPE_STATIC(OSPFD, OPAQUE_INFO_PER_ID, "OSPF opaque per-ID info"); + +/*------------------------------------------------------------------------* + * Following are initialize/terminate functions for Opaque-LSAs handling. + *------------------------------------------------------------------------*/ + +#ifdef SUPPORT_OSPF_API +int ospf_apiserver_init(void); +void ospf_apiserver_term(void); +/* Init apiserver? It's disabled by default. */ +int ospf_apiserver_enable; +#endif /* SUPPORT_OSPF_API */ + +static void ospf_opaque_register_vty(void); +static void ospf_opaque_funclist_init(void); +static void ospf_opaque_funclist_term(void); +static void free_opaque_info_per_type_del(void *val); +static void free_opaque_info_per_id(void *val); +static int ospf_opaque_lsa_install_hook(struct ospf_lsa *lsa); +static int ospf_opaque_lsa_delete_hook(struct ospf_lsa *lsa); + +void ospf_opaque_init(void) +{ + ospf_opaque_register_vty(); + ospf_opaque_funclist_init(); + + if (ospf_mpls_te_init() != 0) + exit(1); + + /* Segment Routing init */ + if (ospf_sr_init() != 0) + exit(1); + + if (ospf_router_info_init() != 0) + exit(1); + + if (ospf_ext_init() != 0) + exit(1); + +#ifdef SUPPORT_OSPF_API + if ((ospf_apiserver_enable) && (ospf_apiserver_init() != 0)) + exit(1); +#endif /* SUPPORT_OSPF_API */ + + return; +} + +void ospf_opaque_term(void) +{ + ospf_mpls_te_term(); + + ospf_router_info_term(); + + ospf_ext_term(); + + ospf_sr_term(); + +#ifdef SUPPORT_OSPF_API + ospf_apiserver_term(); +#endif /* SUPPORT_OSPF_API */ + + ospf_opaque_funclist_term(); + return; +} + +void ospf_opaque_finish(void) +{ + ospf_mpls_te_finish(); + + ospf_router_info_finish(); + + ospf_ext_finish(); + +#ifdef SUPPORT_OSPF_API + ospf_apiserver_term(); +#endif + + ospf_sr_finish(); +} + +int ospf_opaque_type9_lsa_init(struct ospf_interface *oi) +{ + if (oi->opaque_lsa_self != NULL) + list_delete(&oi->opaque_lsa_self); + + oi->opaque_lsa_self = list_new(); + oi->opaque_lsa_self->del = free_opaque_info_per_type_del; + oi->t_opaque_lsa_self = NULL; + return 0; +} + +void ospf_opaque_type9_lsa_term(struct ospf_interface *oi) +{ + EVENT_OFF(oi->t_opaque_lsa_self); + if (oi->opaque_lsa_self != NULL) + list_delete(&oi->opaque_lsa_self); + oi->opaque_lsa_self = NULL; + return; +} + +int ospf_opaque_type10_lsa_init(struct ospf_area *area) +{ + if (area->opaque_lsa_self != NULL) + list_delete(&area->opaque_lsa_self); + + area->opaque_lsa_self = list_new(); + area->opaque_lsa_self->del = free_opaque_info_per_type_del; + area->t_opaque_lsa_self = NULL; + +#ifdef MONITOR_LSDB_CHANGE + area->lsdb->new_lsa_hook = ospf_opaque_lsa_install_hook; + area->lsdb->del_lsa_hook = ospf_opaque_lsa_delete_hook; +#endif /* MONITOR_LSDB_CHANGE */ + return 0; +} + +void ospf_opaque_type10_lsa_term(struct ospf_area *area) +{ +#ifdef MONITOR_LSDB_CHANGE + area->lsdb->new_lsa_hook = area->lsdb->del_lsa_hook = NULL; +#endif /* MONITOR_LSDB_CHANGE */ + + EVENT_OFF(area->t_opaque_lsa_self); + if (area->opaque_lsa_self != NULL) + list_delete(&area->opaque_lsa_self); + return; +} + +int ospf_opaque_type11_lsa_init(struct ospf *top) +{ + if (top->opaque_lsa_self != NULL) + list_delete(&top->opaque_lsa_self); + + top->opaque_lsa_self = list_new(); + top->opaque_lsa_self->del = free_opaque_info_per_type_del; + top->t_opaque_lsa_self = NULL; + +#ifdef MONITOR_LSDB_CHANGE + top->lsdb->new_lsa_hook = ospf_opaque_lsa_install_hook; + top->lsdb->del_lsa_hook = ospf_opaque_lsa_delete_hook; +#endif /* MONITOR_LSDB_CHANGE */ + return 0; +} + +void ospf_opaque_type11_lsa_term(struct ospf *top) +{ +#ifdef MONITOR_LSDB_CHANGE + top->lsdb->new_lsa_hook = top->lsdb->del_lsa_hook = NULL; +#endif /* MONITOR_LSDB_CHANGE */ + + EVENT_OFF(top->t_opaque_lsa_self); + if (top->opaque_lsa_self != NULL) + list_delete(&top->opaque_lsa_self); + return; +} + +static const char *ospf_opaque_type_name(uint8_t opaque_type) +{ + const char *name = "Unknown"; + + switch (opaque_type) { + case OPAQUE_TYPE_WILDCARD: /* This is a special assignment! */ + name = "Wildcard"; + break; + case OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA: + name = "Traffic Engineering LSA"; + break; + case OPAQUE_TYPE_SYCAMORE_OPTICAL_TOPOLOGY_DESC: + name = "Sycamore optical topology description"; + break; + case OPAQUE_TYPE_GRACE_LSA: + name = "Grace-LSA"; + break; + case OPAQUE_TYPE_INTER_AS_LSA: + name = "Inter-AS TE-v2 LSA"; + break; + case OPAQUE_TYPE_ROUTER_INFORMATION_LSA: + name = "Router Information LSA"; + break; + case OPAQUE_TYPE_EXTENDED_PREFIX_LSA: + name = "Extended Prefix Opaque LSA"; + break; + case OPAQUE_TYPE_EXTENDED_LINK_LSA: + name = "Extended Link Opaque LSA"; + break; + default: + if (OPAQUE_TYPE_RANGE_UNASSIGNED(opaque_type)) + name = "Unassigned"; + else { + uint32_t bigger_range = opaque_type; + /* + * Get around type-limits warning: comparison is always + * true due to limited range of data type + */ + if (OPAQUE_TYPE_RANGE_RESERVED(bigger_range)) + name = "Private/Experimental"; + } + break; + } + return name; +} + +/*------------------------------------------------------------------------* + * Following are management functions to store user specified callbacks. + *------------------------------------------------------------------------*/ + +struct opaque_info_per_type; /* Forward declaration. */ + +static void free_opaque_info_per_type(struct opaque_info_per_type *oipt, + bool cleanup_owner); + +struct ospf_opaque_functab { + uint8_t opaque_type; + struct opaque_info_per_type *oipt; + + int (*new_if_hook)(struct interface *ifp); + int (*del_if_hook)(struct interface *ifp); + void (*ism_change_hook)(struct ospf_interface *oi, int old_status); + void (*nsm_change_hook)(struct ospf_neighbor *nbr, int old_status); + void (*config_write_router)(struct vty *vty); + void (*config_write_if)(struct vty *vty, struct interface *ifp); + void (*config_write_debug)(struct vty *vty); + void (*show_opaque_info)(struct vty *vty, struct json_object *json, + struct ospf_lsa *lsa); + int (*lsa_originator)(void *arg); + struct ospf_lsa *(*lsa_refresher)(struct ospf_lsa *lsa); + int (*new_lsa_hook)(struct ospf_lsa *lsa); + int (*del_lsa_hook)(struct ospf_lsa *lsa); +}; + +/* Handle LSA-9/10/11 altogether. */ +static struct list *ospf_opaque_wildcard_funclist; +static struct list *ospf_opaque_type9_funclist; +static struct list *ospf_opaque_type10_funclist; +static struct list *ospf_opaque_type11_funclist; + +static void ospf_opaque_del_functab(void *val) +{ + XFREE(MTYPE_OSPF_OPAQUE_FUNCTAB, val); + return; +} + +static void ospf_opaque_funclist_init(void) +{ + struct list *funclist; + + funclist = ospf_opaque_wildcard_funclist = list_new(); + funclist->del = ospf_opaque_del_functab; + + funclist = ospf_opaque_type9_funclist = list_new(); + funclist->del = ospf_opaque_del_functab; + + funclist = ospf_opaque_type10_funclist = list_new(); + funclist->del = ospf_opaque_del_functab; + + funclist = ospf_opaque_type11_funclist = list_new(); + funclist->del = ospf_opaque_del_functab; + return; +} + +static void ospf_opaque_funclist_term(void) +{ + struct list *funclist; + + funclist = ospf_opaque_wildcard_funclist; + list_delete(&funclist); + + funclist = ospf_opaque_type9_funclist; + list_delete(&funclist); + + funclist = ospf_opaque_type10_funclist; + list_delete(&funclist); + + funclist = ospf_opaque_type11_funclist; + list_delete(&funclist); + return; +} + +static struct list *ospf_get_opaque_funclist(uint8_t lsa_type) +{ + struct list *funclist = NULL; + + switch (lsa_type) { + case OPAQUE_TYPE_WILDCARD: + /* XXX + * This is an ugly trick to handle type-9/10/11 LSA altogether. + * Yes, "OPAQUE_TYPE_WILDCARD (value 0)" is not an LSA-type, nor + * an officially assigned opaque-type. + * Though it is possible that the value might be officially used + * in the future, we use it internally as a special label, for + * now. + */ + funclist = ospf_opaque_wildcard_funclist; + break; + case OSPF_OPAQUE_LINK_LSA: + funclist = ospf_opaque_type9_funclist; + break; + case OSPF_OPAQUE_AREA_LSA: + funclist = ospf_opaque_type10_funclist; + break; + case OSPF_OPAQUE_AS_LSA: + funclist = ospf_opaque_type11_funclist; + break; + default: + flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", + __func__, lsa_type); + break; + } + return funclist; +} + +/* XXX: such a huge argument list can /not/ be healthy... */ +int ospf_register_opaque_functab( + uint8_t lsa_type, uint8_t opaque_type, + int (*new_if_hook)(struct interface *ifp), + int (*del_if_hook)(struct interface *ifp), + void (*ism_change_hook)(struct ospf_interface *oi, int old_status), + void (*nsm_change_hook)(struct ospf_neighbor *nbr, int old_status), + void (*config_write_router)(struct vty *vty), + void (*config_write_if)(struct vty *vty, struct interface *ifp), + void (*config_write_debug)(struct vty *vty), + void (*show_opaque_info)(struct vty *vty, struct json_object *json, + struct ospf_lsa *lsa), + int (*lsa_originator)(void *arg), + struct ospf_lsa *(*lsa_refresher)(struct ospf_lsa *lsa), + int (*new_lsa_hook)(struct ospf_lsa *lsa), + int (*del_lsa_hook)(struct ospf_lsa *lsa)) +{ + struct list *funclist; + struct ospf_opaque_functab *new; + + if ((funclist = ospf_get_opaque_funclist(lsa_type)) == NULL) + return -1; + + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + + for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) + if (functab->opaque_type == opaque_type) { + flog_warn( + EC_OSPF_LSA, + "%s: Duplicated entry?: lsa_type(%u), opaque_type(%u)", + __func__, lsa_type, opaque_type); + return -1; + } + + new = XCALLOC(MTYPE_OSPF_OPAQUE_FUNCTAB, + sizeof(struct ospf_opaque_functab)); + + new->opaque_type = opaque_type; + new->oipt = NULL; + new->new_if_hook = new_if_hook; + new->del_if_hook = del_if_hook; + new->ism_change_hook = ism_change_hook; + new->nsm_change_hook = nsm_change_hook; + new->config_write_router = config_write_router; + new->config_write_if = config_write_if; + new->config_write_debug = config_write_debug; + new->show_opaque_info = show_opaque_info; + new->lsa_originator = lsa_originator; + new->lsa_refresher = lsa_refresher; + new->new_lsa_hook = new_lsa_hook; + new->del_lsa_hook = del_lsa_hook; + + listnode_add(funclist, new); + + return 0; +} + +void ospf_delete_opaque_functab(uint8_t lsa_type, uint8_t opaque_type) +{ + struct list *funclist; + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + + if ((funclist = ospf_get_opaque_funclist(lsa_type)) != NULL) + for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) { + if (functab->opaque_type == opaque_type) { + /* Cleanup internal control information, if it + * still remains. */ + if (functab->oipt != NULL) + free_opaque_info_per_type(functab->oipt, + true); + /* Dequeue listnode entry from the list. */ + listnode_delete(funclist, functab); + + XFREE(MTYPE_OSPF_OPAQUE_FUNCTAB, functab); + break; + } + } + + return; +} + +static struct ospf_opaque_functab * +ospf_opaque_functab_lookup(struct ospf_lsa *lsa) +{ + struct list *funclist; + struct listnode *node; + struct ospf_opaque_functab *functab; + uint8_t key = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)); + + if ((funclist = ospf_get_opaque_funclist(lsa->data->type)) != NULL) + for (ALL_LIST_ELEMENTS_RO(funclist, node, functab)) + if (functab->opaque_type == key) + return functab; + + return NULL; +} + +/*------------------------------------------------------------------------* + * Following are management functions for self-originated LSA entries. + *------------------------------------------------------------------------*/ + +/* + * Opaque-LSA control information per opaque-type. + * Single Opaque-Type may have multiple instances; each of them will be + * identified by their opaque-id. + */ +struct opaque_info_per_type { + uint8_t lsa_type; + uint8_t opaque_type; + + enum { PROC_NORMAL, PROC_SUSPEND } status; + + /* + * Thread for (re-)origination scheduling for this opaque-type. + * + * Initial origination of Opaque-LSAs is controlled by generic + * Opaque-LSA handling module so that same opaque-type entries are + * called all at once when certain conditions are met. + * However, there might be cases that some Opaque-LSA clients need + * to (re-)originate their own Opaque-LSAs out-of-sync with others. + * This thread is prepared for that specific purpose. + */ + struct event *t_opaque_lsa_self; + + /* + * Backpointer to an "owner" which is LSA-type dependent. + * type-9: struct ospf_interface + * type-10: struct ospf_area + * type-11: struct ospf + */ + void *owner; + + /* Collection of callback functions for this opaque-type. */ + struct ospf_opaque_functab *functab; + + /* List of Opaque-LSA control information per opaque-id. */ + struct list *id_list; +}; + +/* Opaque-LSA control information per opaque-id. */ +struct opaque_info_per_id { + uint32_t opaque_id; + + /* Thread for refresh/flush scheduling for this opaque-type/id. */ + struct event *t_opaque_lsa_self; + + /* Backpointer to Opaque-LSA control information per opaque-type. */ + struct opaque_info_per_type *opqctl_type; + + /* Here comes an actual Opaque-LSA entry for this opaque-type/id. */ + struct ospf_lsa *lsa; +}; + +static struct opaque_info_per_type * +register_opaque_info_per_type(struct ospf_opaque_functab *functab, + struct ospf_lsa *new); +static struct opaque_info_per_type * +lookup_opaque_info_by_type(struct ospf_lsa *lsa); +static struct opaque_info_per_id * +register_opaque_info_per_id(struct opaque_info_per_type *oipt, + struct ospf_lsa *new); +static struct opaque_info_per_id * +lookup_opaque_info_by_id(struct opaque_info_per_type *oipt, + struct ospf_lsa *lsa); +static struct opaque_info_per_id *register_opaque_lsa(struct ospf_lsa *new); + + +static struct opaque_info_per_type * +register_opaque_info_per_type(struct ospf_opaque_functab *functab, + struct ospf_lsa *new) +{ + struct ospf *top; + struct opaque_info_per_type *oipt; + + oipt = XCALLOC(MTYPE_OPAQUE_INFO_PER_TYPE, + sizeof(struct opaque_info_per_type)); + + switch (new->data->type) { + case OSPF_OPAQUE_LINK_LSA: + oipt->owner = new->oi; + listnode_add(new->oi->opaque_lsa_self, oipt); + break; + case OSPF_OPAQUE_AREA_LSA: + oipt->owner = new->area; + listnode_add(new->area->opaque_lsa_self, oipt); + break; + case OSPF_OPAQUE_AS_LSA: + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (new->area != NULL && (top = new->area->ospf) == NULL) { + free_opaque_info_per_type(oipt, true); + oipt = NULL; + goto out; /* This case may not exist. */ + } + oipt->owner = top; + listnode_add(top->opaque_lsa_self, oipt); + break; + default: + flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", + __func__, new->data->type); + free_opaque_info_per_type(oipt, true); + oipt = NULL; + goto out; /* This case may not exist. */ + } + + oipt->lsa_type = new->data->type; + oipt->opaque_type = GET_OPAQUE_TYPE(ntohl(new->data->id.s_addr)); + oipt->status = PROC_NORMAL; + oipt->functab = functab; + functab->oipt = oipt; + oipt->id_list = list_new(); + oipt->id_list->del = free_opaque_info_per_id; + +out: + return oipt; +} + +static void free_opaque_info_per_type(struct opaque_info_per_type *oipt, + bool cleanup_owner) +{ + struct opaque_info_per_id *oipi; + struct ospf_lsa *lsa; + struct listnode *node, *nnode; + struct list *l; + + /* Control information per opaque-id may still exist. */ + for (ALL_LIST_ELEMENTS(oipt->id_list, node, nnode, oipi)) { + if ((lsa = oipi->lsa) == NULL) + continue; + if (IS_LSA_MAXAGE(lsa)) + continue; + ospf_opaque_lsa_flush_schedule(lsa); + } + + EVENT_OFF(oipt->t_opaque_lsa_self); + list_delete(&oipt->id_list); + if (cleanup_owner) { + /* Remove from its owner's self-originated LSA list. */ + switch (oipt->lsa_type) { + case OSPF_OPAQUE_LINK_LSA: + l = ((struct ospf_interface *)oipt->owner) + ->opaque_lsa_self; + break; + case OSPF_OPAQUE_AREA_LSA: + l = ((struct ospf_area *)oipt->owner)->opaque_lsa_self; + break; + case OSPF_OPAQUE_AS_LSA: + l = ((struct ospf *)oipt->owner)->opaque_lsa_self; + break; + default: + flog_warn( + EC_OSPF_LSA_UNEXPECTED, + "free_opaque_info_owner: Unexpected LSA-type(%u)", + oipt->lsa_type); + return; + } + listnode_delete(l, oipt); + } + XFREE(MTYPE_OPAQUE_INFO_PER_TYPE, oipt); + return; +} + +static void free_opaque_info_per_type_del(void *val) +{ + free_opaque_info_per_type((struct opaque_info_per_type *)val, false); +} + +static struct opaque_info_per_type * +lookup_opaque_info_by_type(struct ospf_lsa *lsa) +{ + struct ospf *top; + struct ospf_area *area; + struct ospf_interface *oi; + struct list *listtop = NULL; + struct listnode *node, *nnode; + struct opaque_info_per_type *oipt = NULL; + uint8_t key = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)); + + switch (lsa->data->type) { + case OSPF_OPAQUE_LINK_LSA: + if ((oi = lsa->oi) != NULL) + listtop = oi->opaque_lsa_self; + else + flog_warn( + EC_OSPF_LSA, + "Type-9 Opaque-LSA: Reference to OI is missing?"); + break; + case OSPF_OPAQUE_AREA_LSA: + if ((area = lsa->area) != NULL) + listtop = area->opaque_lsa_self; + else + flog_warn( + EC_OSPF_LSA, + "Type-10 Opaque-LSA: Reference to AREA is missing?"); + break; + case OSPF_OPAQUE_AS_LSA: + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if ((area = lsa->area) != NULL && (top = area->ospf) == NULL) { + flog_warn( + EC_OSPF_LSA, + "Type-11 Opaque-LSA: Reference to OSPF is missing?"); + break; /* Unlikely to happen. */ + } + listtop = top->opaque_lsa_self; + break; + default: + flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", + __func__, lsa->data->type); + break; + } + + if (listtop != NULL) + for (ALL_LIST_ELEMENTS(listtop, node, nnode, oipt)) + if (oipt->opaque_type == key) + return oipt; + + return NULL; +} + +static struct opaque_info_per_id * +register_opaque_info_per_id(struct opaque_info_per_type *oipt, + struct ospf_lsa *new) +{ + struct opaque_info_per_id *oipi; + + oipi = XCALLOC(MTYPE_OPAQUE_INFO_PER_ID, + sizeof(struct opaque_info_per_id)); + + oipi->opaque_id = GET_OPAQUE_ID(ntohl(new->data->id.s_addr)); + oipi->opqctl_type = oipt; + oipi->lsa = ospf_lsa_lock(new); + + listnode_add(oipt->id_list, oipi); + + return oipi; +} + +static void free_opaque_info_per_id(void *val) +{ + struct opaque_info_per_id *oipi = (struct opaque_info_per_id *)val; + + EVENT_OFF(oipi->t_opaque_lsa_self); + if (oipi->lsa != NULL) + ospf_lsa_unlock(&oipi->lsa); + XFREE(MTYPE_OPAQUE_INFO_PER_ID, oipi); + return; +} + +static struct opaque_info_per_id * +lookup_opaque_info_by_id(struct opaque_info_per_type *oipt, + struct ospf_lsa *lsa) +{ + struct listnode *node, *nnode; + struct opaque_info_per_id *oipi; + uint32_t key = GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr)); + + for (ALL_LIST_ELEMENTS(oipt->id_list, node, nnode, oipi)) + if (oipi->opaque_id == key) + return oipi; + + return NULL; +} + +static struct opaque_info_per_id *register_opaque_lsa(struct ospf_lsa *new) +{ + struct ospf_opaque_functab *functab; + struct opaque_info_per_type *oipt; + struct opaque_info_per_id *oipi = NULL; + + if ((functab = ospf_opaque_functab_lookup(new)) == NULL) + goto out; + + if ((oipt = lookup_opaque_info_by_type(new)) == NULL + && (oipt = register_opaque_info_per_type(functab, new)) == NULL) + goto out; + + if ((oipi = register_opaque_info_per_id(oipt, new)) == NULL) + goto out; + +out: + return oipi; +} + +int ospf_opaque_is_owned(struct ospf_lsa *lsa) +{ + struct opaque_info_per_type *oipt = lookup_opaque_info_by_type(lsa); + + return (oipt != NULL && lookup_opaque_info_by_id(oipt, lsa) != NULL); +} + +/*------------------------------------------------------------------------* + * Following are (vty) configuration functions for Opaque-LSAs handling. + *------------------------------------------------------------------------*/ + +DEFUN (capability_opaque, + capability_opaque_cmd, + "capability opaque", + "Enable specific OSPF feature\n" + "Opaque LSA\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + /* Check that OSPF is using default VRF */ + if (ospf->vrf_id != VRF_DEFAULT) { + vty_out(vty, + "OSPF Opaque LSA is only supported in default VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Turn on the "master switch" of opaque-lsa capability. */ + if (!CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Opaque capability: OFF -> ON"); + + SET_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE); + ospf_renegotiate_optional_capabilities(ospf); + } + return CMD_SUCCESS; +} + +DEFUN (ospf_opaque, + ospf_opaque_cmd, + "ospf opaque-lsa", + "OSPF specific commands\n" + "Enable the Opaque-LSA capability (rfc2370)\n") +{ + return capability_opaque(self, vty, argc, argv); +} + +DEFUN (no_capability_opaque, + no_capability_opaque_cmd, + "no capability opaque", + NO_STR + "Enable specific OSPF feature\n" + "Opaque LSA\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + /* Turn off the "master switch" of opaque-lsa capability. */ + if (CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Opaque capability: ON -> OFF"); + + UNSET_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE); + ospf_renegotiate_optional_capabilities(ospf); + } + return CMD_SUCCESS; +} + +DEFUN (no_ospf_opaque, + no_ospf_opaque_cmd, + "no ospf opaque-lsa", + NO_STR + "OSPF specific commands\n" + "Enable the Opaque-LSA capability (rfc2370)\n") +{ + return no_capability_opaque(self, vty, argc, argv); +} + +static void ospf_opaque_register_vty(void) +{ + install_element(OSPF_NODE, &capability_opaque_cmd); + install_element(OSPF_NODE, &no_capability_opaque_cmd); + install_element(OSPF_NODE, &ospf_opaque_cmd); + install_element(OSPF_NODE, &no_ospf_opaque_cmd); + return; +} + +/*------------------------------------------------------------------------* + * Following are collection of user-registered function callers. + *------------------------------------------------------------------------*/ + +static int opaque_lsa_new_if_callback(struct list *funclist, + struct interface *ifp) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + int rc = -1; + + for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) + if (functab->new_if_hook != NULL) + if ((*functab->new_if_hook)(ifp) != 0) + goto out; + rc = 0; +out: + return rc; +} + +static int opaque_lsa_del_if_callback(struct list *funclist, + struct interface *ifp) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + int rc = -1; + + for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) + if (functab->del_if_hook != NULL) + if ((*functab->del_if_hook)(ifp) != 0) + goto out; + rc = 0; +out: + return rc; +} + +static void opaque_lsa_ism_change_callback(struct list *funclist, + struct ospf_interface *oi, + int old_status) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + + for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) + if (functab->ism_change_hook != NULL) + (*functab->ism_change_hook)(oi, old_status); + + return; +} + +static void opaque_lsa_nsm_change_callback(struct list *funclist, + struct ospf_neighbor *nbr, + int old_status) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + + for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) + if (functab->nsm_change_hook != NULL) + (*functab->nsm_change_hook)(nbr, old_status); + return; +} + +static void opaque_lsa_config_write_router_callback(struct list *funclist, + struct vty *vty) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + + for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) + if (functab->config_write_router != NULL) + (*functab->config_write_router)(vty); + return; +} + +static void opaque_lsa_config_write_if_callback(struct list *funclist, + struct vty *vty, + struct interface *ifp) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + + for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) + if (functab->config_write_if != NULL) + (*functab->config_write_if)(vty, ifp); + return; +} + +static void opaque_lsa_config_write_debug_callback(struct list *funclist, + struct vty *vty) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + + for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) + if (functab->config_write_debug != NULL) + (*functab->config_write_debug)(vty); + return; +} + +static int opaque_lsa_originate_callback(struct list *funclist, + void *lsa_type_dependent) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + int rc = -1; + + for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) + if (functab->lsa_originator != NULL) + if ((*functab->lsa_originator)(lsa_type_dependent) != 0) + goto out; + rc = 0; +out: + return rc; +} + +static int new_lsa_callback(struct list *funclist, struct ospf_lsa *lsa) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + int rc = -1; + + /* This function handles ALL types of LSAs, not only opaque ones. */ + for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) + if (functab->new_lsa_hook != NULL) + if ((*functab->new_lsa_hook)(lsa) != 0) + goto out; + rc = 0; +out: + return rc; +} + +static int del_lsa_callback(struct list *funclist, struct ospf_lsa *lsa) +{ + struct listnode *node, *nnode; + struct ospf_opaque_functab *functab; + int rc = -1; + + /* This function handles ALL types of LSAs, not only opaque ones. */ + for (ALL_LIST_ELEMENTS(funclist, node, nnode, functab)) + if (functab->del_lsa_hook != NULL) + if ((*functab->del_lsa_hook)(lsa) != 0) + goto out; + rc = 0; +out: + return rc; +} + +/*------------------------------------------------------------------------* + * Following are glue functions to call Opaque-LSA specific processing. + *------------------------------------------------------------------------*/ + +int ospf_opaque_new_if(struct interface *ifp) +{ + struct list *funclist; + int rc = -1; + + funclist = ospf_opaque_wildcard_funclist; + if (opaque_lsa_new_if_callback(funclist, ifp) != 0) + goto out; + + funclist = ospf_opaque_type9_funclist; + if (opaque_lsa_new_if_callback(funclist, ifp) != 0) + goto out; + + funclist = ospf_opaque_type10_funclist; + if (opaque_lsa_new_if_callback(funclist, ifp) != 0) + goto out; + + funclist = ospf_opaque_type11_funclist; + if (opaque_lsa_new_if_callback(funclist, ifp) != 0) + goto out; + + rc = 0; +out: + return rc; +} + +int ospf_opaque_del_if(struct interface *ifp) +{ + struct list *funclist; + int rc = -1; + + funclist = ospf_opaque_wildcard_funclist; + if (opaque_lsa_del_if_callback(funclist, ifp) != 0) + goto out; + + funclist = ospf_opaque_type9_funclist; + if (opaque_lsa_del_if_callback(funclist, ifp) != 0) + goto out; + + funclist = ospf_opaque_type10_funclist; + if (opaque_lsa_del_if_callback(funclist, ifp) != 0) + goto out; + + funclist = ospf_opaque_type11_funclist; + if (opaque_lsa_del_if_callback(funclist, ifp) != 0) + goto out; + + rc = 0; +out: + return rc; +} + +void ospf_opaque_ism_change(struct ospf_interface *oi, int old_status) +{ + struct list *funclist; + + funclist = ospf_opaque_wildcard_funclist; + opaque_lsa_ism_change_callback(funclist, oi, old_status); + + funclist = ospf_opaque_type9_funclist; + opaque_lsa_ism_change_callback(funclist, oi, old_status); + + funclist = ospf_opaque_type10_funclist; + opaque_lsa_ism_change_callback(funclist, oi, old_status); + + funclist = ospf_opaque_type11_funclist; + opaque_lsa_ism_change_callback(funclist, oi, old_status); + + return; +} + +void ospf_opaque_nsm_change(struct ospf_neighbor *nbr, int old_state) +{ + struct ospf *top; + struct list *funclist; + + if ((top = oi_to_top(nbr->oi)) == NULL) + goto out; + + if (old_state != NSM_Full && nbr->state == NSM_Full) { + if (CHECK_FLAG(nbr->options, OSPF_OPTION_O)) { + if (!CHECK_FLAG(top->opaque, + OPAQUE_OPERATION_READY_BIT)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Opaque-LSA: Now get operational!"); + + SET_FLAG(top->opaque, + OPAQUE_OPERATION_READY_BIT); + } + + ospf_opaque_lsa_originate_schedule(nbr->oi, NULL); + } + } else if (old_state == NSM_Full && nbr->state != NSM_Full) { +#ifdef NOTYET +/* + * If no more opaque-capable full-state neighbor remains in the + * flooding scope which corresponds to Opaque-LSA type, periodic + * LS flooding should be stopped. + */ +#endif /* NOTYET */ + ; + } + + funclist = ospf_opaque_wildcard_funclist; + opaque_lsa_nsm_change_callback(funclist, nbr, old_state); + + funclist = ospf_opaque_type9_funclist; + opaque_lsa_nsm_change_callback(funclist, nbr, old_state); + + funclist = ospf_opaque_type10_funclist; + opaque_lsa_nsm_change_callback(funclist, nbr, old_state); + + funclist = ospf_opaque_type11_funclist; + opaque_lsa_nsm_change_callback(funclist, nbr, old_state); + +out: + return; +} + +void ospf_opaque_config_write_router(struct vty *vty, struct ospf *ospf) +{ + struct list *funclist; + + if (CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) + vty_out(vty, " capability opaque\n"); + + funclist = ospf_opaque_wildcard_funclist; + opaque_lsa_config_write_router_callback(funclist, vty); + + funclist = ospf_opaque_type9_funclist; + opaque_lsa_config_write_router_callback(funclist, vty); + + funclist = ospf_opaque_type10_funclist; + opaque_lsa_config_write_router_callback(funclist, vty); + + funclist = ospf_opaque_type11_funclist; + opaque_lsa_config_write_router_callback(funclist, vty); + + return; +} + +void ospf_opaque_config_write_if(struct vty *vty, struct interface *ifp) +{ + struct list *funclist; + + funclist = ospf_opaque_wildcard_funclist; + opaque_lsa_config_write_if_callback(funclist, vty, ifp); + + funclist = ospf_opaque_type9_funclist; + opaque_lsa_config_write_if_callback(funclist, vty, ifp); + + funclist = ospf_opaque_type10_funclist; + opaque_lsa_config_write_if_callback(funclist, vty, ifp); + + funclist = ospf_opaque_type11_funclist; + opaque_lsa_config_write_if_callback(funclist, vty, ifp); + + return; +} + +void ospf_opaque_config_write_debug(struct vty *vty) +{ + struct list *funclist; + + funclist = ospf_opaque_wildcard_funclist; + opaque_lsa_config_write_debug_callback(funclist, vty); + + funclist = ospf_opaque_type9_funclist; + opaque_lsa_config_write_debug_callback(funclist, vty); + + funclist = ospf_opaque_type10_funclist; + opaque_lsa_config_write_debug_callback(funclist, vty); + + funclist = ospf_opaque_type11_funclist; + opaque_lsa_config_write_debug_callback(funclist, vty); + + return; +} + +void show_opaque_info_detail(struct vty *vty, struct ospf_lsa *lsa, + json_object *json) +{ + char buf[128], *bp; + struct lsa_header *lsah = lsa->data; + uint32_t lsid = ntohl(lsah->id.s_addr); + uint8_t opaque_type = GET_OPAQUE_TYPE(lsid); + uint32_t opaque_id = GET_OPAQUE_ID(lsid); + struct ospf_opaque_functab *functab; + int len, lenValid; + + /* Switch output functionality by vty address. */ + if (vty != NULL) { + if (!json) { + vty_out(vty, " Opaque-Type %u (%s)\n", opaque_type, + ospf_opaque_type_name(opaque_type)); + vty_out(vty, " Opaque-ID 0x%x\n", opaque_id); + + vty_out(vty, " Opaque-Info: %u octets of data%s\n", + ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE, + VALID_OPAQUE_INFO_LEN(lsah) + ? "" + : "(Invalid length?)"); + } else { + json_object_string_add( + json, "opaqueType", + ospf_opaque_type_name(opaque_type)); + json_object_int_add(json, "opaqueId", opaque_id); + len = ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE; + json_object_int_add(json, "opaqueDataLength", len); + lenValid = VALID_OPAQUE_INFO_LEN(lsah); + json_object_boolean_add(json, "opaqueDataLengthValid", + lenValid); + if (lenValid) { + bp = asnprintfrr(MTYPE_TMP, buf, sizeof(buf), + "%*pHXn", (int)len, + (lsah + 1)); + json_object_string_add(json, "opaqueData", buf); + if (bp != buf) + XFREE(MTYPE_TMP, bp); + } + } + } else { + zlog_debug(" Opaque-Type %u (%s)", opaque_type, + ospf_opaque_type_name(opaque_type)); + zlog_debug(" Opaque-ID 0x%x", opaque_id); + + zlog_debug(" Opaque-Info: %u octets of data%s", + ntohs(lsah->length) - OSPF_LSA_HEADER_SIZE, + VALID_OPAQUE_INFO_LEN(lsah) ? "" + : "(Invalid length?)"); + } + + /* Call individual output functions. */ + if ((functab = ospf_opaque_functab_lookup(lsa)) != NULL) + if (functab->show_opaque_info != NULL) + (*functab->show_opaque_info)(vty, json, lsa); + + return; +} + +void ospf_opaque_lsa_dump(struct stream *s, uint16_t length) +{ + struct ospf_lsa lsa = {}; + + lsa.data = (struct lsa_header *)stream_pnt(s); + lsa.size = length; + show_opaque_info_detail(NULL, &lsa, NULL); + return; +} + +static int ospf_opaque_lsa_install_hook(struct ospf_lsa *lsa) +{ + struct list *funclist; + int rc = -1; + + /* + * Some Opaque-LSA user may want to monitor every LSA installation + * into the LSDB, regardless with target LSA type. + */ + funclist = ospf_opaque_wildcard_funclist; + if (new_lsa_callback(funclist, lsa) != 0) + goto out; + + funclist = ospf_opaque_type9_funclist; + if (new_lsa_callback(funclist, lsa) != 0) + goto out; + + funclist = ospf_opaque_type10_funclist; + if (new_lsa_callback(funclist, lsa) != 0) + goto out; + + funclist = ospf_opaque_type11_funclist; + if (new_lsa_callback(funclist, lsa) != 0) + goto out; + + rc = 0; +out: + return rc; +} + +static int ospf_opaque_lsa_delete_hook(struct ospf_lsa *lsa) +{ + struct list *funclist; + int rc = -1; + + /* + * Some Opaque-LSA user may want to monitor every LSA deletion + * from the LSDB, regardless with target LSA type. + */ + funclist = ospf_opaque_wildcard_funclist; + if (del_lsa_callback(funclist, lsa) != 0) + goto out; + + funclist = ospf_opaque_type9_funclist; + if (del_lsa_callback(funclist, lsa) != 0) + goto out; + + funclist = ospf_opaque_type10_funclist; + if (del_lsa_callback(funclist, lsa) != 0) + goto out; + + funclist = ospf_opaque_type11_funclist; + if (del_lsa_callback(funclist, lsa) != 0) + goto out; + + rc = 0; +out: + return rc; +} + +/*------------------------------------------------------------------------* + * Following are Opaque-LSA origination/refresh management functions. + *------------------------------------------------------------------------*/ + +static void ospf_opaque_type9_lsa_originate(struct event *t); +static void ospf_opaque_type10_lsa_originate(struct event *t); +static void ospf_opaque_type11_lsa_originate(struct event *t); +static void ospf_opaque_lsa_reoriginate_resume(struct list *listtop, void *arg); + +void ospf_opaque_lsa_originate_schedule(struct ospf_interface *oi, int *delay0) +{ + struct ospf *top; + struct ospf_area *area; + struct listnode *node, *nnode; + struct opaque_info_per_type *oipt; + int delay = 0; + + if ((top = oi_to_top(oi)) == NULL || (area = oi->area) == NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Invalid argument?", __func__); + return; + } + + /* It may not a right time to schedule origination now. */ + if (!CHECK_FLAG(top->opaque, OPAQUE_OPERATION_READY_BIT)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Not operational.", __func__); + return; /* This is not an error. */ + } + + if (delay0 != NULL) + delay = *delay0; + + /* + * There might be some entries that have been waiting for triggering + * of per opaque-type re-origination get resumed. + */ + ospf_opaque_lsa_reoriginate_resume(oi->opaque_lsa_self, (void *)oi); + ospf_opaque_lsa_reoriginate_resume(area->opaque_lsa_self, (void *)area); + ospf_opaque_lsa_reoriginate_resume(top->opaque_lsa_self, (void *)top); + + /* + * Now, schedule origination of all Opaque-LSAs per opaque-type. + */ + if (!list_isempty(ospf_opaque_type9_funclist) + && list_isempty(oi->opaque_lsa_self) + && oi->t_opaque_lsa_self == NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Schedule Type-9 Opaque-LSA origination in %d ms later.", + delay); + oi->t_opaque_lsa_self = NULL; + event_add_timer_msec(master, ospf_opaque_type9_lsa_originate, + oi, delay, &oi->t_opaque_lsa_self); + delay += top->min_ls_interval; + } + + if (!list_isempty(ospf_opaque_type10_funclist) + && list_isempty(area->opaque_lsa_self) + && area->t_opaque_lsa_self == NULL) { + /* + * One AREA may contain multiple OIs, but above 2nd and 3rd + * conditions prevent from scheduling the originate function + * again and again. + */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Schedule Type-10 Opaque-LSA origination in %d ms later.", + delay); + area->t_opaque_lsa_self = NULL; + event_add_timer_msec(master, ospf_opaque_type10_lsa_originate, + area, delay, &area->t_opaque_lsa_self); + delay += top->min_ls_interval; + } + + if (!list_isempty(ospf_opaque_type11_funclist) + && list_isempty(top->opaque_lsa_self) + && top->t_opaque_lsa_self == NULL) { + /* + * One OSPF may contain multiple AREAs, but above 2nd and 3rd + * conditions prevent from scheduling the originate function + * again and again. + */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Schedule Type-11 Opaque-LSA origination in %d ms later.", + delay); + top->t_opaque_lsa_self = NULL; + event_add_timer_msec(master, ospf_opaque_type11_lsa_originate, + top, delay, &top->t_opaque_lsa_self); + delay += top->min_ls_interval; + } + + /* + * Following section treats a special situation that this node's + * opaque capability has changed as "ON -> OFF -> ON". + */ + if (!list_isempty(ospf_opaque_type9_funclist) + && !list_isempty(oi->opaque_lsa_self)) { + for (ALL_LIST_ELEMENTS(oi->opaque_lsa_self, node, nnode, + oipt)) { + /* + * removed the test for + * (! list_isempty (oipt->id_list)) * Handler is + * already active. * + * because opaque cababilities ON -> OFF -> ON result in + * list_isempty (oipt->id_list) + * not being empty. + */ + if (oipt->t_opaque_lsa_self + != NULL /* Waiting for a thread call. */ + || oipt->status == PROC_SUSPEND) /* Cannot + originate + now. */ + continue; + + ospf_opaque_lsa_reoriginate_schedule( + (void *)oi, OSPF_OPAQUE_LINK_LSA, + oipt->opaque_type); + } + } + + if (!list_isempty(ospf_opaque_type10_funclist) + && !list_isempty(area->opaque_lsa_self)) { + for (ALL_LIST_ELEMENTS(area->opaque_lsa_self, node, nnode, + oipt)) { + /* + * removed the test for + * (! list_isempty (oipt->id_list)) * Handler is + * already active. * + * because opaque cababilities ON -> OFF -> ON result in + * list_isempty (oipt->id_list) + * not being empty. + */ + if (oipt->t_opaque_lsa_self + != NULL /* Waiting for a thread call. */ + || oipt->status == PROC_SUSPEND) /* Cannot + originate + now. */ + continue; + + ospf_opaque_lsa_reoriginate_schedule( + (void *)area, OSPF_OPAQUE_AREA_LSA, + oipt->opaque_type); + } + } + + if (!list_isempty(ospf_opaque_type11_funclist) + && !list_isempty(top->opaque_lsa_self)) { + for (ALL_LIST_ELEMENTS(top->opaque_lsa_self, node, nnode, + oipt)) { + /* + * removed the test for + * (! list_isempty (oipt->id_list)) * Handler is + * already active. * + * because opaque cababilities ON -> OFF -> ON result in + * list_isempty (oipt->id_list) + * not being empty. + */ + if (oipt->t_opaque_lsa_self + != NULL /* Waiting for a thread call. */ + || oipt->status == PROC_SUSPEND) /* Cannot + originate + now. */ + continue; + + ospf_opaque_lsa_reoriginate_schedule((void *)top, + OSPF_OPAQUE_AS_LSA, + oipt->opaque_type); + } + } + + if (delay0 != NULL) + *delay0 = delay; +} + +static void ospf_opaque_type9_lsa_originate(struct event *t) +{ + struct ospf_interface *oi; + + oi = EVENT_ARG(t); + oi->t_opaque_lsa_self = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Timer[Type9-LSA]: Originate Opaque-LSAs for OI %s", + IF_NAME(oi)); + + opaque_lsa_originate_callback(ospf_opaque_type9_funclist, oi); +} + +static void ospf_opaque_type10_lsa_originate(struct event *t) +{ + struct ospf_area *area; + + area = EVENT_ARG(t); + area->t_opaque_lsa_self = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Timer[Type10-LSA]: Originate Opaque-LSAs for Area %pI4", + &area->area_id); + + opaque_lsa_originate_callback(ospf_opaque_type10_funclist, area); +} + +static void ospf_opaque_type11_lsa_originate(struct event *t) +{ + struct ospf *top; + + top = EVENT_ARG(t); + top->t_opaque_lsa_self = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Timer[Type11-LSA]: Originate AS-External Opaque-LSAs"); + + opaque_lsa_originate_callback(ospf_opaque_type11_funclist, top); +} + +static void ospf_opaque_lsa_reoriginate_resume(struct list *listtop, void *arg) +{ + struct listnode *node, *nnode; + struct opaque_info_per_type *oipt; + struct ospf_opaque_functab *functab; + + if (listtop == NULL) + goto out; + + /* + * Pickup oipt entries those which in SUSPEND status, and give + * them a chance to start re-origination now. + */ + for (ALL_LIST_ELEMENTS(listtop, node, nnode, oipt)) { + if (oipt->status != PROC_SUSPEND) + continue; + + oipt->status = PROC_NORMAL; + + if ((functab = oipt->functab) == NULL + || functab->lsa_originator == NULL) + continue; + + if ((*functab->lsa_originator)(arg) != 0) { + flog_warn(EC_OSPF_LSA, "%s: Failed (opaque-type=%u)", + __func__, oipt->opaque_type); + continue; + } + } + +out: + return; +} + +struct ospf_lsa *ospf_opaque_lsa_install(struct ospf_lsa *lsa, int rt_recalc) +{ + struct ospf_lsa *new = NULL; + struct opaque_info_per_type *oipt; + struct opaque_info_per_id *oipi; + struct ospf *top; + + /* Don't take "rt_recalc" into consideration for now. */ /* XXX */ + + if (!IS_LSA_SELF(lsa)) { + new = lsa; /* Don't touch this LSA. */ + goto out; + } + + if (IS_DEBUG_OSPF(lsa, LSA_INSTALL)) + zlog_debug( + "Install Type-%u Opaque-LSA: [opaque-type=%u, opaque-id=%x]", + lsa->data->type, + GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)), + GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr))); + + /* Replace the existing lsa with the new one. */ + if ((oipt = lookup_opaque_info_by_type(lsa)) != NULL + && (oipi = lookup_opaque_info_by_id(oipt, lsa)) != NULL) { + ospf_lsa_unlock(&oipi->lsa); + oipi->lsa = ospf_lsa_lock(lsa); + } + /* Register the new lsa entry */ + else if (register_opaque_lsa(lsa) == NULL) { + flog_warn(EC_OSPF_LSA, "%s: register_opaque_lsa() ?", __func__); + goto out; + } + + /* + * Make use of a common mechanism (ospf_lsa_refresh_walker) + * for periodic refresh of self-originated Opaque-LSAs. + */ + switch (lsa->data->type) { + case OSPF_OPAQUE_LINK_LSA: + if ((top = oi_to_top(lsa->oi)) == NULL) { + /* Above conditions must have passed. */ + flog_warn(EC_OSPF_LSA, "%s: Something wrong?", + __func__); + goto out; + } + break; + case OSPF_OPAQUE_AREA_LSA: + if (lsa->area == NULL || (top = lsa->area->ospf) == NULL) { + /* Above conditions must have passed. */ + flog_warn(EC_OSPF_LSA, "%s: Something wrong?", + __func__); + goto out; + } + break; + case OSPF_OPAQUE_AS_LSA: + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (lsa->area != NULL && (top = lsa->area->ospf) == NULL) { + /* Above conditions must have passed. */ + flog_warn(EC_OSPF_LSA, "%s: Something wrong?", + __func__); + goto out; + } + break; + default: + flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", + __func__, lsa->data->type); + goto out; + } + + ospf_refresher_register_lsa(top, lsa); + new = lsa; + +out: + return new; +} + +struct ospf_lsa *ospf_opaque_lsa_refresh(struct ospf_lsa *lsa) +{ + struct ospf *ospf; + struct ospf_opaque_functab *functab; + struct ospf_lsa *new = NULL; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + if ((functab = ospf_opaque_functab_lookup(lsa)) == NULL + || functab->lsa_refresher == NULL) { + /* + * Though this LSA seems to have originated on this node, the + * handling module for this "lsa-type and opaque-type" was + * already deleted sometime ago. + * Anyway, this node still has a responsibility to flush this + * LSA from the routing domain. + */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("LSA[Type%d:%pI4]: Flush stray Opaque-LSA", + lsa->data->type, &lsa->data->id); + + lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + ospf_lsa_flush(ospf, lsa); + } else + new = (*functab->lsa_refresher)(lsa); + + return new; +} + +/*------------------------------------------------------------------------* + * Following are re-origination/refresh/flush operations of Opaque-LSAs, + * triggered by external interventions (vty session, signaling, etc). + *------------------------------------------------------------------------*/ + +#define OSPF_OPAQUE_TIMER_ON(T, F, L, V) \ + event_add_timer_msec(master, (F), (L), (V), &(T)) + +static struct ospf_lsa *pseudo_lsa(struct ospf_interface *oi, + struct ospf_area *area, uint8_t lsa_type, + uint8_t opaque_type); +static void ospf_opaque_type9_lsa_reoriginate_timer(struct event *t); +static void ospf_opaque_type10_lsa_reoriginate_timer(struct event *t); +static void ospf_opaque_type11_lsa_reoriginate_timer(struct event *t); +static void ospf_opaque_lsa_refresh_timer(struct event *t); + +void ospf_opaque_lsa_reoriginate_schedule(void *lsa_type_dependent, + uint8_t lsa_type, uint8_t opaque_type) +{ + struct ospf *top = NULL; + struct ospf_area dummy, *area = NULL; + struct ospf_interface *oi = NULL; + + struct ospf_lsa *lsa; + struct opaque_info_per_type *oipt; + void (*func)(struct event * t) = NULL; + int delay; + + switch (lsa_type) { + case OSPF_OPAQUE_LINK_LSA: + if ((oi = (struct ospf_interface *)lsa_type_dependent) + == NULL) { + flog_warn(EC_OSPF_LSA, + "%s: Type-9 Opaque-LSA: Invalid parameter?", + __func__); + goto out; + } + if ((top = oi_to_top(oi)) == NULL) { + flog_warn(EC_OSPF_LSA, "%s: OI(%s) -> TOP?", __func__, + IF_NAME(oi)); + goto out; + } + if (!list_isempty(ospf_opaque_type9_funclist) + && list_isempty(oi->opaque_lsa_self) + && oi->t_opaque_lsa_self != NULL) { + flog_warn( + EC_OSPF_LSA, + "Type-9 Opaque-LSA (opaque_type=%u): Common origination for OI(%s) has already started", + opaque_type, IF_NAME(oi)); + goto out; + } + func = ospf_opaque_type9_lsa_reoriginate_timer; + break; + case OSPF_OPAQUE_AREA_LSA: + if ((area = (struct ospf_area *)lsa_type_dependent) == NULL) { + flog_warn(EC_OSPF_LSA, + "%s: Type-10 Opaque-LSA: Invalid parameter?", + __func__); + goto out; + } + if ((top = area->ospf) == NULL) { + flog_warn(EC_OSPF_LSA, "%s: AREA(%pI4) -> TOP?", + __func__, &area->area_id); + goto out; + } + if (!list_isempty(ospf_opaque_type10_funclist) + && list_isempty(area->opaque_lsa_self) + && area->t_opaque_lsa_self != NULL) { + flog_warn( + EC_OSPF_LSA, + "Type-10 Opaque-LSA (opaque_type=%u): Common origination for AREA(%pI4) has already started", + opaque_type, &area->area_id); + goto out; + } + func = ospf_opaque_type10_lsa_reoriginate_timer; + break; + case OSPF_OPAQUE_AS_LSA: + if ((top = (struct ospf *)lsa_type_dependent) == NULL) { + flog_warn(EC_OSPF_LSA, + "%s: Type-11 Opaque-LSA: Invalid parameter?", + __func__); + goto out; + } + if (!list_isempty(ospf_opaque_type11_funclist) + && list_isempty(top->opaque_lsa_self) + && top->t_opaque_lsa_self != NULL) { + flog_warn( + EC_OSPF_LSA, + "Type-11 Opaque-LSA (opaque_type=%u): Common origination has already started", + opaque_type); + goto out; + } + + /* Fake "area" to pass "ospf" to a lookup function later. */ + dummy.ospf = top; + area = &dummy; + + func = ospf_opaque_type11_lsa_reoriginate_timer; + break; + default: + flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", + __func__, lsa_type); + goto out; + } + + /* It may not a right time to schedule reorigination now. */ + if (!CHECK_FLAG(top->opaque, OPAQUE_OPERATION_READY_BIT)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Not operational.", __func__); + goto out; /* This is not an error. */ + } + + /* Generate a dummy lsa to be passed for a lookup function. */ + lsa = pseudo_lsa(oi, area, lsa_type, opaque_type); + lsa->vrf_id = VRF_DEFAULT; + + if ((oipt = lookup_opaque_info_by_type(lsa)) == NULL) { + struct ospf_opaque_functab *functab; + if ((functab = ospf_opaque_functab_lookup(lsa)) == NULL) { + flog_warn( + EC_OSPF_LSA, + "%s: No associated function?: lsa_type(%u), opaque_type(%u)", + __func__, lsa_type, opaque_type); + goto out; + } + if ((oipt = register_opaque_info_per_type(functab, lsa)) + == NULL) { + flog_warn( + EC_OSPF_LSA, + "%s: Cannot get a control info?: lsa_type(%u), opaque_type(%u)", + __func__, lsa_type, opaque_type); + goto out; + } + } + + if (oipt->t_opaque_lsa_self != NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Type-%u Opaque-LSA has already scheduled to RE-ORIGINATE: [opaque-type=%u]", + lsa_type, + GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr))); + goto out; + } + + /* + * Different from initial origination time, in which various conditions + * (opaque capability, neighbor status etc) are assured by caller of + * the originating function "ospf_opaque_lsa_originate_schedule ()", + * it is highly possible that these conditions might not be satisfied + * at the time of re-origination function is to be called. + */ + delay = top->min_ls_interval; /* XXX */ + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Schedule Type-%u Opaque-LSA to RE-ORIGINATE in %d ms later: [opaque-type=%u]", + lsa_type, delay, + GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr))); + + OSPF_OPAQUE_TIMER_ON(oipt->t_opaque_lsa_self, func, oipt, delay); + +out: + return; +} + +static struct ospf_lsa *pseudo_lsa(struct ospf_interface *oi, + struct ospf_area *area, uint8_t lsa_type, + uint8_t opaque_type) +{ + static struct ospf_lsa lsa = {0}; + static struct lsa_header lsah = {0}; + uint32_t tmp; + + lsa.oi = oi; + lsa.area = area; + lsa.data = &lsah; + lsa.vrf_id = VRF_DEFAULT; + + lsah.type = lsa_type; + tmp = SET_OPAQUE_LSID(opaque_type, 0); /* Opaque-ID is unused here. */ + lsah.id.s_addr = htonl(tmp); + + return &lsa; +} + +static void ospf_opaque_type9_lsa_reoriginate_timer(struct event *t) +{ + struct opaque_info_per_type *oipt; + struct ospf_opaque_functab *functab; + struct ospf *top; + struct ospf_interface *oi; + + oipt = EVENT_ARG(t); + + if ((functab = oipt->functab) == NULL + || functab->lsa_originator == NULL) { + flog_warn(EC_OSPF_LSA, "%s: No associated function?", __func__); + return; + } + + oi = (struct ospf_interface *)oipt->owner; + if ((top = oi_to_top(oi)) == NULL) { + flog_warn(EC_OSPF_LSA, "%s: Something wrong?", __func__); + return; + } + + if (!CHECK_FLAG(top->config, OSPF_OPAQUE_CAPABLE) || + !OSPF_IF_PARAM(oi, opaque_capable) || !ospf_if_is_enable(oi) || + ospf_nbr_count_opaque_capable(oi) == 0) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Suspend re-origination of Type-9 Opaque-LSAs (opaque-type=%u) for a while...", + oipt->opaque_type); + + oipt->status = PROC_SUSPEND; + return; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Timer[Type9-LSA]: Re-originate Opaque-LSAs (opaque-type=%u) for OI (%s)", + oipt->opaque_type, IF_NAME(oi)); + + (*functab->lsa_originator)(oi); +} + +static void ospf_opaque_type10_lsa_reoriginate_timer(struct event *t) +{ + struct opaque_info_per_type *oipt; + struct ospf_opaque_functab *functab; + struct listnode *node, *nnode; + struct ospf *top; + struct ospf_area *area; + struct ospf_interface *oi; + int n; + + oipt = EVENT_ARG(t); + + if ((functab = oipt->functab) == NULL + || functab->lsa_originator == NULL) { + flog_warn(EC_OSPF_LSA, "%s: No associated function?", __func__); + return; + } + + area = (struct ospf_area *)oipt->owner; + if (area == NULL || (top = area->ospf) == NULL) { + flog_warn(EC_OSPF_LSA, "%s: Something wrong?", __func__); + return; + } + + /* There must be at least one "opaque-capable, full-state" neighbor. */ + n = 0; + for (ALL_LIST_ELEMENTS(area->oiflist, node, nnode, oi)) { + if ((n = ospf_nbr_count_opaque_capable(oi)) > 0) + break; + } + + if (n == 0 || !CHECK_FLAG(top->config, OSPF_OPAQUE_CAPABLE)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Suspend re-origination of Type-10 Opaque-LSAs (opaque-type=%u) for a while...", + oipt->opaque_type); + + oipt->status = PROC_SUSPEND; + return; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Timer[Type10-LSA]: Re-originate Opaque-LSAs (opaque-type=%u) for Area %pI4", + oipt->opaque_type, &area->area_id); + + (*functab->lsa_originator)(area); +} + +static void ospf_opaque_type11_lsa_reoriginate_timer(struct event *t) +{ + struct opaque_info_per_type *oipt; + struct ospf_opaque_functab *functab; + struct ospf *top; + + oipt = EVENT_ARG(t); + + if ((functab = oipt->functab) == NULL + || functab->lsa_originator == NULL) { + flog_warn(EC_OSPF_LSA, "%s: No associated function?", __func__); + return; + } + + if ((top = (struct ospf *)oipt->owner) == NULL) { + flog_warn(EC_OSPF_LSA, "%s: Something wrong?", __func__); + return; + } + + if (!CHECK_FLAG(top->config, OSPF_OPAQUE_CAPABLE)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Suspend re-origination of Type-11 Opaque-LSAs (opaque-type=%u) for a while...", + oipt->opaque_type); + + oipt->status = PROC_SUSPEND; + return; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Timer[Type11-LSA]: Re-originate Opaque-LSAs (opaque-type=%u).", + oipt->opaque_type); + + (*functab->lsa_originator)(top); +} + +void ospf_opaque_lsa_refresh_schedule(struct ospf_lsa *lsa0) +{ + struct opaque_info_per_type *oipt; + struct opaque_info_per_id *oipi; + struct ospf_lsa *lsa; + struct ospf *top; + int delay; + + if ((oipt = lookup_opaque_info_by_type(lsa0)) == NULL + || (oipi = lookup_opaque_info_by_id(oipt, lsa0)) == NULL) { + flog_warn(EC_OSPF_LSA, "%s: Invalid parameter?", __func__); + goto out; + } + + /* Given "lsa0" and current "oipi->lsa" may different, but harmless. */ + if ((lsa = oipi->lsa) == NULL) { + flog_warn(EC_OSPF_LSA, "%s: Something wrong?", __func__); + goto out; + } + + if (oipi->t_opaque_lsa_self != NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Type-%u Opaque-LSA has already scheduled to REFRESH: [opaque-type=%u, opaque-id=%x]", + lsa->data->type, + GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)), + GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr))); + goto out; + } + + /* Delete this lsa from neighbor retransmit-list. */ + switch (lsa->data->type) { + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + ospf_ls_retransmit_delete_nbr_area(lsa->area, lsa); + break; + case OSPF_OPAQUE_AS_LSA: + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if ((lsa0->area != NULL) && (lsa0->area->ospf != NULL)) + top = lsa0->area->ospf; + ospf_ls_retransmit_delete_nbr_as(top, lsa); + break; + default: + flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", + __func__, lsa->data->type); + goto out; + } + + delay = ospf_lsa_refresh_delay(lsa); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Schedule Type-%u Opaque-LSA to REFRESH in %d sec later: [opaque-type=%u, opaque-id=%x]", + lsa->data->type, delay, + GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)), + GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr))); + + OSPF_OPAQUE_TIMER_ON(oipi->t_opaque_lsa_self, + ospf_opaque_lsa_refresh_timer, oipi, delay * 1000); +out: + return; +} + +static void ospf_opaque_lsa_refresh_timer(struct event *t) +{ + struct opaque_info_per_id *oipi; + struct ospf_opaque_functab *functab; + struct ospf_lsa *lsa; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Timer[Opaque-LSA]: (Opaque-LSA Refresh expire)"); + + oipi = EVENT_ARG(t); + + if ((lsa = oipi->lsa) != NULL) + if ((functab = oipi->opqctl_type->functab) != NULL) + if (functab->lsa_refresher != NULL) + (*functab->lsa_refresher)(lsa); +} + +void ospf_opaque_lsa_flush_schedule(struct ospf_lsa *lsa0) +{ + struct opaque_info_per_type *oipt; + struct opaque_info_per_id *oipi; + struct ospf_lsa *lsa; + struct ospf *top; + + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + if ((oipt = lookup_opaque_info_by_type(lsa0)) == NULL + || (oipi = lookup_opaque_info_by_id(oipt, lsa0)) == NULL) { + flog_warn(EC_OSPF_LSA, "%s: Invalid parameter?", __func__); + goto out; + } + + /* Given "lsa0" and current "oipi->lsa" may different, but harmless. */ + if ((lsa = oipi->lsa) == NULL) { + flog_warn(EC_OSPF_LSA, "%s: Something wrong?", __func__); + goto out; + } + + if (lsa->opaque_zero_len_delete && + lsa->data->length != htons(sizeof(struct lsa_header))) { + /* minimize the size of the withdrawal: */ + /* increment the sequence number and make len just header */ + /* and update checksum */ + lsa->data->ls_seqnum = lsa_seqnum_increment(lsa); + lsa->data->length = htons(sizeof(struct lsa_header)); + lsa->data->checksum = 0; + lsa->data->checksum = ospf_lsa_checksum(lsa->data); + } + + /* Delete this lsa from neighbor retransmit-list. */ + switch (lsa->data->type) { + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + ospf_ls_retransmit_delete_nbr_area(lsa->area, lsa); + break; + case OSPF_OPAQUE_AS_LSA: + if ((lsa0->area != NULL) && (lsa0->area->ospf != NULL)) + top = lsa0->area->ospf; + ospf_ls_retransmit_delete_nbr_as(top, lsa); + break; + default: + flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", + __func__, lsa->data->type); + 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); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Schedule Type-%u Opaque-LSA to FLUSH: [opaque-type=%u, opaque-id=%x]", + lsa->data->type, + GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)), + GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr))); + + /* Disassociate internal control information with the given lsa. */ + free_opaque_info_per_id((void *)oipi); + +out: + return; +} + +void ospf_opaque_self_originated_lsa_received(struct ospf_neighbor *nbr, + struct ospf_lsa *lsa) +{ + struct ospf *top; + + if ((top = oi_to_top(nbr->oi)) == NULL) + return; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type%d:%pI4]: processing self-originated Opaque-LSA", + lsa->data->type, &lsa->data->id); + + /* + * Install the stale LSA into the Link State Database, add it to the + * MaxAge list, and flush it from the OSPF routing domain. For other + * LSA types, the installation is done in the refresh function. It is + * done inline here since the opaque refresh function is dynamically + * registered when opaque LSAs are originated (which is not the case + * for stale LSAs). + */ + lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + ospf_lsa_install( + top, (lsa->data->type == OSPF_OPAQUE_LINK_LSA) ? nbr->oi : NULL, + lsa); + ospf_lsa_maxage(top, lsa); + + switch (lsa->data->type) { + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + ospf_flood_through_area(nbr->oi->area, NULL /*inbr*/, lsa); + break; + case OSPF_OPAQUE_AS_LSA: + ospf_flood_through_as(top, NULL /*inbr*/, lsa); + break; + default: + flog_warn(EC_OSPF_LSA_UNEXPECTED, "%s: Unexpected LSA-type(%u)", + __func__, lsa->data->type); + return; + } +} + +/*------------------------------------------------------------------------* + * Following are util functions; probably be used by Opaque-LSAs only... + *------------------------------------------------------------------------*/ + +struct ospf *oi_to_top(struct ospf_interface *oi) +{ + struct ospf *top = NULL; + struct ospf_area *area; + + if (oi == NULL || (area = oi->area) == NULL + || (top = area->ospf) == NULL) + flog_warn(EC_OSPF_LSA, + "Broken relationship for \"OI -> AREA -> OSPF\"?"); + + return top; +} diff --git a/ospfd/ospf_opaque.h b/ospfd/ospf_opaque.h new file mode 100644 index 0000000..e0c78cd --- /dev/null +++ b/ospfd/ospf_opaque.h @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This is an implementation of rfc2370. + * Copyright (C) 2001 KDD R&D Laboratories, Inc. + * http://www.kddlabs.co.jp/ + */ + +#ifndef _ZEBRA_OSPF_OPAQUE_H +#define _ZEBRA_OSPF_OPAQUE_H + +#include "vty.h" +#include + +#define IS_OPAQUE_LSA(type) \ + ((type) == OSPF_OPAQUE_LINK_LSA || (type) == OSPF_OPAQUE_AREA_LSA \ + || (type) == OSPF_OPAQUE_AS_LSA) + +/* + * Opaque LSA's link state ID is redefined as follows. + * + * 24 16 8 0 + * +--------+--------+--------+--------+ + * |tttttttt|........|........|........| + * +--------+--------+--------+--------+ + * |<-Type->|<------- Opaque ID ------>| + */ +#define LSID_OPAQUE_TYPE_MASK 0xff000000 /* 8 bits */ +#define LSID_OPAQUE_ID_MASK 0x00ffffff /* 24 bits */ + +#define GET_OPAQUE_TYPE(lsid) (((uint32_t)(lsid)&LSID_OPAQUE_TYPE_MASK) >> 24) + +#define GET_OPAQUE_ID(lsid) ((uint32_t)(lsid)&LSID_OPAQUE_ID_MASK) + +#define SET_OPAQUE_LSID(type, id) \ + ((((unsigned)(type) << 24) & LSID_OPAQUE_TYPE_MASK) \ + | ((id)&LSID_OPAQUE_ID_MASK)) + +/* + * Opaque LSA types will be assigned by IANA. + * + */ +#define OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA 1 +#define OPAQUE_TYPE_SYCAMORE_OPTICAL_TOPOLOGY_DESC 2 +#define OPAQUE_TYPE_GRACE_LSA 3 +#define OPAQUE_TYPE_L1VPN_LSA 5 +#define OPAQUE_TYPE_ROUTER_INFORMATION_LSA 4 +#define OPAQUE_TYPE_INTER_AS_LSA 6 +#define OPAQUE_TYPE_EXTENDED_PREFIX_LSA 7 +#define OPAQUE_TYPE_EXTENDED_LINK_LSA 8 +#define OPAQUE_TYPE_MAX 8 + +/* Following types are proposed in internet-draft documents. */ +#define OPAQUE_TYPE_8021_QOSPF 129 +#define OPAQUE_TYPE_SECONDARY_NEIGHBOR_DISCOVERY 224 +#define OPAQUE_TYPE_FLOODGATE 225 + +/* Ugly hack to make use of an unallocated value for wildcard matching! */ +#define OPAQUE_TYPE_WILDCARD 0 + +#define OPAQUE_TYPE_RANGE_UNASSIGNED(type) \ + (OPAQUE_TYPE_MAX <= (type) && (type) <= 127) + +#define OPAQUE_TYPE_RANGE_RESERVED(type) (127 < (type) && (type) <= 255) + +#define OSPF_OPAQUE_LSA_MIN_SIZE 0 /* RFC5250 imposes no minimum */ + +#define VALID_OPAQUE_INFO_LEN(lsahdr) \ + ((ntohs((lsahdr)->length) >= sizeof(struct lsa_header)) \ + && ((ntohs((lsahdr)->length) < OSPF_MAX_LSA_SIZE)) \ + && ((ntohs((lsahdr)->length) % sizeof(uint32_t)) == 0)) + +/* + * Following section defines generic TLV (type, length, value) macros, + * used for various LSA opaque usage e.g. Traffic Engineering. + */ +struct tlv_header { + uint16_t type; /* Type of Value */ + uint16_t length; /* Length of Value portion only, in bytes */ +}; + +#define TLV_HDR_SIZE (sizeof(struct tlv_header)) + +#define TLV_BODY_SIZE(tlvh) (ROUNDUP(ntohs((tlvh)->length), sizeof(uint32_t))) + +#define TLV_SIZE(tlvh) (uint32_t)(TLV_HDR_SIZE + TLV_BODY_SIZE(tlvh)) + +#define TLV_HDR_TOP(lsah) \ + (struct tlv_header *)((char *)(lsah) + OSPF_LSA_HEADER_SIZE) + +#define TLV_HDR_NEXT(tlvh) \ + (struct tlv_header *)((char *)(tlvh) + TLV_SIZE(tlvh)) + +#define TLV_HDR_SUBTLV(tlvh) \ + (struct tlv_header *)((char *)(tlvh) + TLV_HDR_SIZE) + +#define TLV_DATA(tlvh) (void *)((char *)(tlvh) + TLV_HDR_SIZE) + +#define TLV_TYPE(tlvh) tlvh.header.type +#define TLV_LEN(tlvh) tlvh.header.length +#define TLV_HDR(tlvh) tlvh.header + +/* Following declaration concerns the Opaque LSA management */ +enum lsa_opcode { REORIGINATE_THIS_LSA, REFRESH_THIS_LSA, FLUSH_THIS_LSA }; + +/* Prototypes. */ + +extern void ospf_opaque_init(void); +extern void ospf_opaque_term(void); +extern void ospf_opaque_finish(void); +extern int ospf_opaque_type9_lsa_init(struct ospf_interface *oi); +extern void ospf_opaque_type9_lsa_term(struct ospf_interface *oi); +extern int ospf_opaque_type10_lsa_init(struct ospf_area *area); +extern void ospf_opaque_type10_lsa_term(struct ospf_area *area); +extern int ospf_opaque_type11_lsa_init(struct ospf *ospf); +extern void ospf_opaque_type11_lsa_term(struct ospf *ospf); + +extern int ospf_register_opaque_functab( + uint8_t lsa_type, uint8_t opaque_type, + int (*new_if_hook)(struct interface *ifp), + int (*del_if_hook)(struct interface *ifp), + void (*ism_change_hook)(struct ospf_interface *oi, int old_status), + void (*nsm_change_hook)(struct ospf_neighbor *nbr, int old_status), + void (*config_write_router)(struct vty *vty), + void (*config_write_if)(struct vty *vty, struct interface *ifp), + void (*config_write_debug)(struct vty *vty), + void (*show_opaque_info)(struct vty *vty, struct json_object *json, + struct ospf_lsa *lsa), + int (*lsa_originator)(void *arg), + struct ospf_lsa *(*lsa_refresher)(struct ospf_lsa *lsa), + int (*new_lsa_hook)(struct ospf_lsa *lsa), + int (*del_lsa_hook)(struct ospf_lsa *lsa)); +extern void ospf_delete_opaque_functab(uint8_t lsa_type, uint8_t opaque_type); + +extern int ospf_opaque_new_if(struct interface *ifp); +extern int ospf_opaque_del_if(struct interface *ifp); +extern void ospf_opaque_ism_change(struct ospf_interface *oi, int old_status); +extern void ospf_opaque_nsm_change(struct ospf_neighbor *nbr, int old_status); +extern void ospf_opaque_config_write_router(struct vty *vty, struct ospf *ospf); +extern void ospf_opaque_config_write_if(struct vty *vty, struct interface *ifp); +extern void ospf_opaque_config_write_debug(struct vty *vty); +extern void show_opaque_info_detail(struct vty *vty, struct ospf_lsa *lsa, + json_object *json); +extern void ospf_opaque_lsa_dump(struct stream *s, uint16_t length); + +extern void ospf_opaque_lsa_originate_schedule(struct ospf_interface *oi, + int *init_delay); +extern struct ospf_lsa *ospf_opaque_lsa_install(struct ospf_lsa *lsa, + int rt_recalc); +extern struct ospf_lsa *ospf_opaque_lsa_refresh(struct ospf_lsa *lsa); + +extern void ospf_opaque_lsa_reoriginate_schedule(void *lsa_type_dependent, + uint8_t lsa_type, + uint8_t opaque_type); +extern void ospf_opaque_lsa_refresh_schedule(struct ospf_lsa *lsa); +extern void ospf_opaque_lsa_flush_schedule(struct ospf_lsa *lsa); + +extern void ospf_opaque_self_originated_lsa_received(struct ospf_neighbor *nbr, + struct ospf_lsa *lsa); +extern struct ospf *oi_to_top(struct ospf_interface *oi); + +extern int ospf_opaque_is_owned(struct ospf_lsa *lsa); + +#endif /* _ZEBRA_OSPF_OPAQUE_H */ diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c new file mode 100644 index 0000000..b37efa3 --- /dev/null +++ b/ospfd/ospf_packet.c @@ -0,0 +1,4023 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF Sending and Receiving OSPF Packets. + * Copyright (C) 1999, 2000 Toshiaki Takada + */ + +#include + +#include "monotime.h" +#include "frrevent.h" +#include "memory.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "sockunion.h" +#include "stream.h" +#include "log.h" +#include "sockopt.h" +#include "checksum.h" +#ifdef CRYPTO_INTERNAL +#include "md5.h" +#endif +#include "vrf.h" +#include "lib_errors.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_network.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_errors.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_gr.h" +#include "ospfd/ospf_auth.h" + +/* + * OSPF Fragmentation / fragmented writes + * + * ospfd can support writing fragmented packets, for cases where + * kernel will not fragment IP_HDRINCL and/or multicast destined + * packets (ie TTBOMK all kernels, BSD, SunOS, Linux). However, + * SunOS, probably BSD too, clobber the user supplied IP ID and IP + * flags fields, hence user-space fragmentation will not work. + * Only Linux is known to leave IP header unmolested. + * Further, fragmentation really should be done the kernel, which already + * supports it, and which avoids nasty IP ID state problems. + * + * Fragmentation of OSPF packets can be required on networks with router + * with many many interfaces active in one area, or on networks with links + * with low MTUs. + */ +#ifdef GNU_LINUX +#define WANT_OSPF_WRITE_FRAGMENT +#endif + +/* Packet Type String. */ +const struct message ospf_packet_type_str[] = { + {OSPF_MSG_HELLO, "Hello"}, + {OSPF_MSG_DB_DESC, "Database Description"}, + {OSPF_MSG_LS_REQ, "Link State Request"}, + {OSPF_MSG_LS_UPD, "Link State Update"}, + {OSPF_MSG_LS_ACK, "Link State Acknowledgment"}, + {0}}; + +/* Minimum (besides OSPF_HEADER_SIZE) lengths for OSPF packets of + particular types, offset is the "type" field of a packet. */ +static const uint16_t ospf_packet_minlen[] = { + 0, + OSPF_HELLO_MIN_SIZE, + OSPF_DB_DESC_MIN_SIZE, + OSPF_LS_REQ_MIN_SIZE, + OSPF_LS_UPD_MIN_SIZE, + OSPF_LS_ACK_MIN_SIZE, +}; + +/* Minimum (besides OSPF_LSA_HEADER_SIZE) lengths for LSAs of particular + types, offset is the "LSA type" field. */ +static const uint16_t ospf_lsa_minlen[] = { + 0, /* OSPF_UNKNOWN_LSA */ + OSPF_ROUTER_LSA_MIN_SIZE, /* OSPF_ROUTER_LSA */ + OSPF_NETWORK_LSA_MIN_SIZE, /* OSPF_NETWORK_LSA */ + OSPF_SUMMARY_LSA_MIN_SIZE, /* OSPF_SUMMARY_LSA */ + OSPF_SUMMARY_LSA_MIN_SIZE, /* OSPF_ASBR_SUMMARY_LSA */ + OSPF_AS_EXTERNAL_LSA_MIN_SIZE, /* OSPF_AS_EXTERNAL_LSA */ + 0, /* Unsupported, OSPF_GROUP_MEMBER_LSA */ + OSPF_AS_EXTERNAL_LSA_MIN_SIZE, /* OSPF_AS_NSSA_LSA */ + 0, /* Unsupported, OSPF_EXTERNAL_ATTRIBURES_LSA */ + OSPF_OPAQUE_LSA_MIN_SIZE, /* OSPF_OPAQUE_LINK_LSA */ + OSPF_OPAQUE_LSA_MIN_SIZE, /* OSPF_OPAQUE_AREA_LSA */ + OSPF_OPAQUE_LSA_MIN_SIZE, /* OSPF_OPAQUE_AS_LSA */ +}; + +static struct ospf_packet *ospf_packet_new(size_t size) +{ + struct ospf_packet *new; + + new = XCALLOC(MTYPE_OSPF_PACKET, sizeof(struct ospf_packet)); + new->s = stream_new(size); + + return new; +} + +void ospf_packet_free(struct ospf_packet *op) +{ + if (op->s) + stream_free(op->s); + + XFREE(MTYPE_OSPF_PACKET, op); +} + +struct ospf_fifo *ospf_fifo_new(void) +{ + struct ospf_fifo *new; + + new = XCALLOC(MTYPE_OSPF_FIFO, sizeof(struct ospf_fifo)); + return new; +} + +/* Add new packet to fifo. */ +void ospf_fifo_push(struct ospf_fifo *fifo, struct ospf_packet *op) +{ + if (fifo->tail) + fifo->tail->next = op; + else + fifo->head = op; + + fifo->tail = op; + + fifo->count++; +} + +/* Add new packet to head of fifo. */ +static void ospf_fifo_push_head(struct ospf_fifo *fifo, struct ospf_packet *op) +{ + op->next = fifo->head; + + if (fifo->tail == NULL) + fifo->tail = op; + + fifo->head = op; + + fifo->count++; +} + +/* Delete first packet from fifo. */ +struct ospf_packet *ospf_fifo_pop(struct ospf_fifo *fifo) +{ + struct ospf_packet *op; + + op = fifo->head; + + if (op) { + fifo->head = op->next; + + if (fifo->head == NULL) + fifo->tail = NULL; + + fifo->count--; + } + + return op; +} + +/* Return first fifo entry. */ +struct ospf_packet *ospf_fifo_head(struct ospf_fifo *fifo) +{ + return fifo->head; +} + +/* Flush ospf packet fifo. */ +void ospf_fifo_flush(struct ospf_fifo *fifo) +{ + struct ospf_packet *op; + struct ospf_packet *next; + + for (op = fifo->head; op; op = next) { + next = op->next; + ospf_packet_free(op); + } + fifo->head = fifo->tail = NULL; + fifo->count = 0; +} + +/* Free ospf packet fifo. */ +void ospf_fifo_free(struct ospf_fifo *fifo) +{ + ospf_fifo_flush(fifo); + + XFREE(MTYPE_OSPF_FIFO, fifo); +} + +static void ospf_packet_add(struct ospf_interface *oi, struct ospf_packet *op) +{ + /* Add packet to end of queue. */ + ospf_fifo_push(oi->obuf, op); + + /* Debug of packet fifo*/ + /* ospf_fifo_debug (oi->obuf); */ +} + +static void ospf_packet_add_top(struct ospf_interface *oi, + struct ospf_packet *op) +{ + /* Add packet to head of queue. */ + ospf_fifo_push_head(oi->obuf, op); + + /* Debug of packet fifo*/ + /* ospf_fifo_debug (oi->obuf); */ +} + +static void ospf_packet_delete(struct ospf_interface *oi) +{ + struct ospf_packet *op; + + op = ospf_fifo_pop(oi->obuf); + + if (op) + ospf_packet_free(op); +} + +static struct ospf_packet *ospf_packet_dup(struct ospf_packet *op) +{ + struct ospf_packet *new; + + if (stream_get_endp(op->s) != op->length) + /* XXX size_t */ + zlog_debug( + "ospf_packet_dup stream %lu ospf_packet %u size mismatch", + (unsigned long)STREAM_SIZE(op->s), op->length); + + /* Reserve space for MD5/HMAC SHA authentication that may be added later. */ + new = ospf_packet_new(stream_get_endp(op->s) + KEYCHAIN_MAX_HASH_SIZE); + stream_copy(new->s, op->s); + + new->dst = op->dst; + new->length = op->length; + + return new; +} + +/* XXX inline */ +static unsigned int ospf_packet_authspace(struct ospf_interface *oi) +{ + int auth = 0; + + if (ospf_auth_type(oi) == OSPF_AUTH_CRYPTOGRAPHIC) + auth = KEYCHAIN_MAX_HASH_SIZE; + + return auth; +} + +static unsigned int ospf_packet_max(struct ospf_interface *oi) +{ + int max; + + max = oi->ifp->mtu - ospf_packet_authspace(oi); + + max -= (OSPF_HEADER_SIZE + sizeof(struct ip)); + + return max; +} + +static void ospf_ls_req_timer(struct event *thread) +{ + struct ospf_neighbor *nbr; + + nbr = EVENT_ARG(thread); + nbr->t_ls_req = NULL; + + /* Send Link State Request. */ + if (ospf_ls_request_count(nbr)) + ospf_ls_req_send(nbr); + + /* Set Link State Request retransmission timer. */ + OSPF_NSM_TIMER_ON(nbr->t_ls_req, ospf_ls_req_timer, nbr->v_ls_req); +} + +void ospf_ls_req_event(struct ospf_neighbor *nbr) +{ + EVENT_OFF(nbr->t_ls_req); + event_add_event(master, ospf_ls_req_timer, nbr, 0, &nbr->t_ls_req); +} + +/* Cyclic timer function. Fist registered in ospf_nbr_new () in + ospf_neighbor.c */ +void ospf_ls_upd_timer(struct event *thread) +{ + struct ospf_neighbor *nbr; + + nbr = EVENT_ARG(thread); + nbr->t_ls_upd = NULL; + + /* Send Link State Update. */ + if (ospf_ls_retransmit_count(nbr) > 0) { + struct list *update; + struct ospf_lsdb *lsdb; + int i; + int retransmit_interval; + + retransmit_interval = + OSPF_IF_PARAM(nbr->oi, retransmit_interval); + + lsdb = &nbr->ls_rxmt; + update = list_new(); + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) { + struct route_table *table = lsdb->type[i].db; + struct route_node *rn; + + for (rn = route_top(table); rn; rn = route_next(rn)) { + struct ospf_lsa *lsa; + + if ((lsa = rn->info) != NULL) { + /* Don't retransmit an LSA if we + received it within + the last RxmtInterval seconds - this + is to allow the + neighbour a chance to acknowledge the + LSA as it may + have ben just received before the + retransmit timer + fired. This is a small tweak to what + is in the RFC, + but it will cut out out a lot of + retransmit traffic + - MAG */ + if (monotime_since(&lsa->tv_recv, NULL) + >= retransmit_interval * 1000000LL) + listnode_add(update, rn->info); + } + } + } + + if (listcount(update) > 0) + ospf_ls_upd_send(nbr, update, OSPF_SEND_PACKET_DIRECT, + 0); + list_delete(&update); + } + + /* Set LS Update retransmission timer. */ + OSPF_NSM_TIMER_ON(nbr->t_ls_upd, ospf_ls_upd_timer, nbr->v_ls_upd); +} + +void ospf_ls_ack_timer(struct event *thread) +{ + struct ospf_interface *oi; + + oi = EVENT_ARG(thread); + oi->t_ls_ack = NULL; + + /* Send Link State Acknowledgment. */ + if (listcount(oi->ls_ack) > 0) + ospf_ls_ack_send_delayed(oi); + + /* Set LS Ack timer. */ + OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack); +} + +#ifdef WANT_OSPF_WRITE_FRAGMENT +static void ospf_write_frags(int fd, struct ospf_packet *op, struct ip *iph, + struct msghdr *msg, unsigned int maxdatasize, + unsigned int mtu, int flags, uint8_t type) +{ +#define OSPF_WRITE_FRAG_SHIFT 3 + uint16_t offset; + struct iovec *iovp; + int ret; + + assert(op->length == stream_get_endp(op->s)); + assert(msg->msg_iovlen == 2); + + /* we can but try. + * + * SunOS, BSD and BSD derived kernels likely will clear ip_id, as + * well as the IP_MF flag, making this all quite pointless. + * + * However, for a system on which IP_MF is left alone, and ip_id left + * alone or else which sets same ip_id for each fragment this might + * work, eg linux. + * + * XXX-TODO: It would be much nicer to have the kernel's use their + * existing fragmentation support to do this for us. Bugs/RFEs need to + * be raised against the various kernels. + */ + + /* set More Frag */ + iph->ip_off |= IP_MF; + + /* ip frag offset is expressed in units of 8byte words */ + offset = maxdatasize >> OSPF_WRITE_FRAG_SHIFT; + + iovp = &msg->msg_iov[1]; + + while ((stream_get_endp(op->s) - stream_get_getp(op->s)) + > maxdatasize) { + /* data length of this frag is to next offset value */ + iovp->iov_len = offset << OSPF_WRITE_FRAG_SHIFT; + iph->ip_len = iovp->iov_len + sizeof(struct ip); + assert(iph->ip_len <= mtu); + + sockopt_iphdrincl_swab_htosys(iph); + + ret = sendmsg(fd, msg, flags); + + sockopt_iphdrincl_swab_systoh(iph); + + if (ret < 0) + flog_err( + EC_LIB_SOCKET, + "*** %s: sendmsg failed to %pI4, id %d, off %d, len %d, mtu %u failed with %s", + __func__, &iph->ip_dst, iph->ip_id, iph->ip_off, + iph->ip_len, mtu, safe_strerror(errno)); + + if (IS_DEBUG_OSPF_PACKET(type - 1, SEND)) { + zlog_debug("%s: sent id %d, off %d, len %d to %pI4", + __func__, iph->ip_id, iph->ip_off, + iph->ip_len, &iph->ip_dst); + } + + iph->ip_off += offset; + stream_forward_getp(op->s, iovp->iov_len); + iovp->iov_base = stream_pnt(op->s); + } + + /* setup for final fragment */ + iovp->iov_len = stream_get_endp(op->s) - stream_get_getp(op->s); + iph->ip_len = iovp->iov_len + sizeof(struct ip); + iph->ip_off &= (~IP_MF); +} +#endif /* WANT_OSPF_WRITE_FRAGMENT */ + +static void ospf_write(struct event *thread) +{ + struct ospf *ospf = EVENT_ARG(thread); + struct ospf_interface *oi; + struct ospf_packet *op; + struct sockaddr_in sa_dst; + struct ip iph; + struct msghdr msg; + struct iovec iov[2]; + uint8_t type; + int ret, fd; + int flags = 0; + struct listnode *node; +#ifdef WANT_OSPF_WRITE_FRAGMENT + static uint16_t ipid = 0; + uint16_t maxdatasize; +#endif /* WANT_OSPF_WRITE_FRAGMENT */ +#define OSPF_WRITE_IPHL_SHIFT 2 + int pkt_count = 0; + +#ifdef GNU_LINUX + unsigned char cmsgbuf[64] = {}; + struct cmsghdr *cm = (struct cmsghdr *)cmsgbuf; + struct in_pktinfo *pi; +#endif + fd = ospf->fd; + + if (fd < 0 || ospf->oi_running == 0) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s failed to send, fd %d, instance %u", + __func__, fd, ospf->oi_running); + return; + } + + node = listhead(ospf->oi_write_q); + assert(node); + oi = listgetdata(node); + +#ifdef WANT_OSPF_WRITE_FRAGMENT + /* seed ipid static with low order bits of time */ + if (ipid == 0) + ipid = (time(NULL) & 0xffff); +#endif /* WANT_OSPF_WRITE_FRAGMENT */ + + while ((pkt_count < ospf->write_oi_count) && oi) { + pkt_count++; +#ifdef WANT_OSPF_WRITE_FRAGMENT + /* convenience - max OSPF data per packet */ + maxdatasize = oi->ifp->mtu - sizeof(struct ip); +#endif /* WANT_OSPF_WRITE_FRAGMENT */ + + /* Reset socket fd to use. */ + fd = ospf->fd; + + /* Check for per-interface socket */ + if (ospf->intf_socket_enabled && + (IF_OSPF_IF_INFO(oi->ifp))->oii_fd > 0) + fd = (IF_OSPF_IF_INFO(oi->ifp))->oii_fd; + + /* Get one packet from queue. */ + op = ospf_fifo_head(oi->obuf); + assert(op); + assert(op->length >= OSPF_HEADER_SIZE); + + if (op->dst.s_addr == htonl(OSPF_ALLSPFROUTERS) + || op->dst.s_addr == htonl(OSPF_ALLDROUTERS)) + ospf_if_ipmulticast(fd, oi->address, oi->ifp->ifindex); + + /* Rewrite the md5 signature & update the seq */ + ospf_auth_make(oi, op); + + /* Retrieve OSPF packet type. */ + stream_set_getp(op->s, 1); + type = stream_getc(op->s); + + /* reset get pointer */ + stream_set_getp(op->s, 0); + + memset(&iph, 0, sizeof(iph)); + memset(&sa_dst, 0, sizeof(sa_dst)); + + sa_dst.sin_family = AF_INET; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sa_dst.sin_len = sizeof(sa_dst); +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ + sa_dst.sin_addr = op->dst; + sa_dst.sin_port = htons(0); + + /* Set DONTROUTE flag if dst is unicast. */ + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + if (!IN_MULTICAST(htonl(op->dst.s_addr))) + flags = MSG_DONTROUTE; + + iph.ip_hl = sizeof(struct ip) >> OSPF_WRITE_IPHL_SHIFT; + /* it'd be very strange for header to not be 4byte-word aligned + * but.. */ + if (sizeof(struct ip) + > (unsigned int)(iph.ip_hl << OSPF_WRITE_IPHL_SHIFT)) + iph.ip_hl++; /* we presume sizeof(struct ip) cant + overflow ip_hl.. */ + + iph.ip_v = IPVERSION; + iph.ip_tos = IPTOS_PREC_INTERNETCONTROL; + iph.ip_len = (iph.ip_hl << OSPF_WRITE_IPHL_SHIFT) + op->length; + +#if defined(__DragonFly__) + /* + * DragonFly's raw socket expects ip_len/ip_off in network byte + * order. + */ + iph.ip_len = htons(iph.ip_len); +#endif + +#ifdef WANT_OSPF_WRITE_FRAGMENT + /* XXX-MT: not thread-safe at all.. + * XXX: this presumes this is only programme sending OSPF + * packets + * otherwise, no guarantee ipid will be unique + */ + iph.ip_id = ++ipid; +#endif /* WANT_OSPF_WRITE_FRAGMENT */ + + iph.ip_off = 0; + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + iph.ip_ttl = OSPF_VL_IP_TTL; + else + iph.ip_ttl = OSPF_IP_TTL; + iph.ip_p = IPPROTO_OSPFIGP; + iph.ip_sum = 0; + iph.ip_src.s_addr = oi->address->u.prefix4.s_addr; + iph.ip_dst.s_addr = op->dst.s_addr; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (caddr_t)&sa_dst; + msg.msg_namelen = sizeof(sa_dst); + msg.msg_iov = iov; + msg.msg_iovlen = 2; + + iov[0].iov_base = (char *)&iph; + iov[0].iov_len = iph.ip_hl << OSPF_WRITE_IPHL_SHIFT; + iov[1].iov_base = stream_pnt(op->s); + iov[1].iov_len = op->length; + +#ifdef GNU_LINUX + msg.msg_control = (caddr_t)cm; + cm->cmsg_level = SOL_IP; + cm->cmsg_type = IP_PKTINFO; + cm->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + pi = (struct in_pktinfo *)CMSG_DATA(cm); + pi->ipi_ifindex = oi->ifp->ifindex; + + msg.msg_controllen = cm->cmsg_len; +#endif + +/* Sadly we can not rely on kernels to fragment packets + * because of either IP_HDRINCL and/or multicast + * destination being set. + */ + +#ifdef WANT_OSPF_WRITE_FRAGMENT + if (op->length > maxdatasize) + ospf_write_frags(fd, op, &iph, &msg, maxdatasize, + oi->ifp->mtu, flags, type); +#endif /* WANT_OSPF_WRITE_FRAGMENT */ + + /* send final fragment (could be first) */ + sockopt_iphdrincl_swab_htosys(&iph); + ret = sendmsg(fd, &msg, flags); + sockopt_iphdrincl_swab_systoh(&iph); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s to %pI4, id %d, off %d, len %d, interface %s, mtu %u:", + __func__, &iph.ip_dst, iph.ip_id, iph.ip_off, + iph.ip_len, oi->ifp->name, oi->ifp->mtu); + + /* sendmsg will return EPERM if firewall is blocking sending. + * This is a normal situation when 'ip nhrp map multicast xxx' + * is being used to send multicast packets to DMVPN peers. In + * that case the original message is blocked with iptables rule + * causing the EPERM result + */ + if (ret < 0 && errno != EPERM) + flog_err( + EC_LIB_SOCKET, + "*** sendmsg in %s failed to %pI4, id %d, off %d, len %d, interface %s, mtu %u: %s", + __func__, &iph.ip_dst, iph.ip_id, iph.ip_off, + iph.ip_len, oi->ifp->name, oi->ifp->mtu, + safe_strerror(errno)); + + /* Show debug sending packet. */ + if (IS_DEBUG_OSPF_PACKET(type - 1, SEND)) { + if (IS_DEBUG_OSPF_PACKET(type - 1, DETAIL)) { + zlog_debug( + "-----------------------------------------------------"); + stream_set_getp(op->s, 0); + ospf_packet_dump(op->s); + } + + zlog_debug("%s sent to [%pI4] via [%s].", + lookup_msg(ospf_packet_type_str, type, NULL), + &op->dst, IF_NAME(oi)); + + if (IS_DEBUG_OSPF_PACKET(type - 1, DETAIL)) + zlog_debug( + "-----------------------------------------------------"); + } + + switch (type) { + case OSPF_MSG_HELLO: + oi->hello_out++; + break; + case OSPF_MSG_DB_DESC: + oi->db_desc_out++; + break; + case OSPF_MSG_LS_REQ: + oi->ls_req_out++; + break; + case OSPF_MSG_LS_UPD: + oi->ls_upd_out++; + break; + case OSPF_MSG_LS_ACK: + oi->ls_ack_out++; + break; + default: + break; + } + + /* Now delete packet from queue. */ + ospf_packet_delete(oi); + + /* Move this interface to the tail of write_q to + serve everyone in a round robin fashion */ + list_delete_node(ospf->oi_write_q, node); + if (ospf_fifo_head(oi->obuf) == NULL) { + oi->on_write_q = 0; + oi = NULL; + } else + listnode_add(ospf->oi_write_q, oi); + + /* Setup to service from the head of the queue again */ + if (!list_isempty(ospf->oi_write_q)) { + node = listhead(ospf->oi_write_q); + oi = listgetdata(node); + } + } + + /* If packets still remain in queue, call write thread. */ + if (!list_isempty(ospf->oi_write_q)) + event_add_write(master, ospf_write, ospf, ospf->fd, + &ospf->t_write); +} + +/* OSPF Hello message read -- RFC2328 Section 10.5. */ +static void ospf_hello(struct ip *iph, struct ospf_header *ospfh, + struct stream *s, struct ospf_interface *oi, int size) +{ + struct ospf_hello *hello; + struct ospf_neighbor *nbr; + int old_state; + struct prefix p; + + /* increment statistics. */ + oi->hello_in++; + + hello = (struct ospf_hello *)stream_pnt(s); + + /* If Hello is myself, silently discard. */ + if (IPV4_ADDR_SAME(&ospfh->router_id, &oi->ospf->router_id)) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) { + zlog_debug( + "ospf_header[%s/%pI4]: selforiginated, dropping.", + lookup_msg(ospf_packet_type_str, ospfh->type, + NULL), + &iph->ip_src); + } + return; + } + + /* get neighbor prefix. */ + p.family = AF_INET; + p.prefixlen = ip_masklen(hello->network_mask); + p.u.prefix4 = iph->ip_src; + + /* Compare network mask. */ + /* Checking is ignored for Point-to-Point and Virtual link. */ + /* Checking is also ignored for Point-to-Multipoint with /32 prefix */ + if (oi->type != OSPF_IFTYPE_POINTOPOINT + && oi->type != OSPF_IFTYPE_VIRTUALLINK + && !(oi->type == OSPF_IFTYPE_POINTOMULTIPOINT + && oi->address->prefixlen == IPV4_MAX_BITLEN)) + if (oi->address->prefixlen != p.prefixlen) { + flog_warn( + EC_OSPF_PACKET, + "Packet %pI4 [Hello:RECV]: NetworkMask mismatch on %s (configured prefix length is %d, but hello packet indicates %d).", + &ospfh->router_id, IF_NAME(oi), + (int)oi->address->prefixlen, (int)p.prefixlen); + return; + } + + /* Compare Router Dead Interval. */ + if (OSPF_IF_PARAM(oi, v_wait) != ntohl(hello->dead_interval)) { + flog_warn( + EC_OSPF_PACKET, + "Packet %pI4 [Hello:RECV]: RouterDeadInterval mismatch on %s (expected %u, but received %u).", + &ospfh->router_id, IF_NAME(oi), + OSPF_IF_PARAM(oi, v_wait), ntohl(hello->dead_interval)); + return; + } + + /* Compare Hello Interval - ignored if fast-hellos are set. */ + if (OSPF_IF_PARAM(oi, fast_hello) == 0) { + if (OSPF_IF_PARAM(oi, v_hello) + != ntohs(hello->hello_interval)) { + flog_warn( + EC_OSPF_PACKET, + "Packet %pI4 [Hello:RECV]: HelloInterval mismatch on %s (expected %u, but received %u).", + &ospfh->router_id, IF_NAME(oi), + OSPF_IF_PARAM(oi, v_hello), + ntohs(hello->hello_interval)); + return; + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Packet %pI4 [Hello:RECV]: Options on %s %s vrf %s", + &ospfh->router_id, IF_NAME(oi), + ospf_options_dump(hello->options), + ospf_vrf_id_to_name(oi->ospf->vrf_id)); + +/* Compare options. */ +#define REJECT_IF_TBIT_ON 1 /* XXX */ +#ifdef REJECT_IF_TBIT_ON + if (CHECK_FLAG(hello->options, OSPF_OPTION_MT)) { + /* + * This router does not support non-zero TOS. + * Drop this Hello packet not to establish neighbor + * relationship. + */ + flog_warn(EC_OSPF_PACKET, + "Packet %pI4 [Hello:RECV]: T-bit ON on %s, drop it.", + &ospfh->router_id, IF_NAME(oi)); + return; + } +#endif /* REJECT_IF_TBIT_ON */ + + if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) && + OSPF_IF_PARAM(oi, opaque_capable) && + CHECK_FLAG(hello->options, OSPF_OPTION_O)) { + /* + * This router does know the correct usage of O-bit + * the bit should be set in DD packet only. + */ + flog_warn(EC_OSPF_PACKET, + "Packet %pI4 [Hello:RECV]: O-bit abuse? on %s", + &ospfh->router_id, IF_NAME(oi)); +#ifdef STRICT_OBIT_USAGE_CHECK + return; /* Reject this packet. */ +#else /* STRICT_OBIT_USAGE_CHECK */ + UNSET_FLAG(hello->options, OSPF_OPTION_O); /* Ignore O-bit. */ +#endif /* STRICT_OBIT_USAGE_CHECK */ + } + + /* new for NSSA is to ensure that NP is on and E is off */ + + if (oi->area->external_routing == OSPF_AREA_NSSA) { + if (!(CHECK_FLAG(OPTIONS(oi), OSPF_OPTION_NP) + && CHECK_FLAG(hello->options, OSPF_OPTION_NP) + && !CHECK_FLAG(OPTIONS(oi), OSPF_OPTION_E) + && !CHECK_FLAG(hello->options, OSPF_OPTION_E))) { + flog_warn( + EC_OSPF_PACKET, + "NSSA-Packet-%pI4[Hello:RECV]: my options: %x, his options %x", + &ospfh->router_id, OPTIONS(oi), + hello->options); + return; + } + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("NSSA-Hello:RECV:Packet from %pI4:", + &ospfh->router_id); + } else + /* The setting of the E-bit found in the Hello Packet's Options + field must match this area's ExternalRoutingCapability A + mismatch causes processing to stop and the packet to be + dropped. The setting of the rest of the bits in the Hello + Packet's Options field should be ignored. */ + if (CHECK_FLAG(OPTIONS(oi), OSPF_OPTION_E) + != CHECK_FLAG(hello->options, OSPF_OPTION_E)) { + flog_warn( + EC_OSPF_PACKET, + "Packet %pI4 [Hello:RECV]: my options: %x, his options %x", + &ospfh->router_id, OPTIONS(oi), + hello->options); + return; + } + + /* get neighbour struct */ + nbr = ospf_nbr_get(oi, ospfh, iph, &p); + + /* neighbour must be valid, ospf_nbr_get creates if none existed */ + assert(nbr); + + old_state = nbr->state; + + /* Add event to thread. */ + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_HelloReceived); + + /* RFC2328 Section 9.5.1 + If the router is not eligible to become Designated Router, + (snip) It must also send an Hello Packet in reply to an + Hello Packet received from any eligible neighbor (other than + the current Designated Router and Backup Designated Router). */ + if (oi->type == OSPF_IFTYPE_NBMA) + if (PRIORITY(oi) == 0 && hello->priority > 0 + && IPV4_ADDR_CMP(&DR(oi), &iph->ip_src) + && IPV4_ADDR_CMP(&BDR(oi), &iph->ip_src)) + OSPF_NSM_TIMER_ON(nbr->t_hello_reply, + ospf_hello_reply_timer, + OSPF_HELLO_REPLY_DELAY); + + /* on NBMA network type, it happens to receive bidirectional Hello + packet + without advance 1-Way Received event. + To avoid incorrect DR-seletion, raise 1-Way Received event.*/ + if (oi->type == OSPF_IFTYPE_NBMA + && (old_state == NSM_Down || old_state == NSM_Attempt)) { + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_OneWayReceived); + nbr->priority = hello->priority; + nbr->d_router = hello->d_router; + nbr->bd_router = hello->bd_router; + return; + } + + if (ospf_nbr_bidirectional(&oi->ospf->router_id, hello->neighbors, + size - OSPF_HELLO_MIN_SIZE)) { + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_TwoWayReceived); + nbr->options |= hello->options; + } else { + /* If the router is DR_OTHER, RESTARTER will not wait + * until it receives the hello from it if it receives + * from DR and BDR. + * So, helper might receives ONW_WAY hello from + * RESTARTER. So not allowing to change the state if it + * receives one_way hellow when it acts as HELPER for + * that specific neighbor. + */ + if (!OSPF_GR_IS_ACTIVE_HELPER(nbr)) + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_OneWayReceived); + /* Set neighbor information. */ + nbr->priority = hello->priority; + nbr->d_router = hello->d_router; + nbr->bd_router = hello->bd_router; + return; + } + + if (OSPF_GR_IS_ACTIVE_HELPER(nbr)) { + /* As per the GR Conformance Test Case 7.2. Section 3 + * "Also, if X was the Designated Router on network segment S + * when the helping relationship began, Y maintains X as the + * Designated Router until the helping relationship is + * terminated." + * When I am helper for this neighbor, I should not trigger the + * ISM Events. Also Intentionally not setting the priority and + * other fields so that when the neighbor exits the Grace + * period, it can handle if there is any change before GR and + * after GR. */ + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "%s, Neighbor is under GR Restart, hence ignoring the ISM Events", + __PRETTY_FUNCTION__); + } else { + /* If neighbor itself declares DR and no BDR exists, + cause event BackupSeen */ + if (IPV4_ADDR_SAME(&nbr->address.u.prefix4, &hello->d_router)) + if (hello->bd_router.s_addr == INADDR_ANY + && oi->state == ISM_Waiting) + OSPF_ISM_EVENT_SCHEDULE(oi, ISM_BackupSeen); + + /* neighbor itself declares BDR. */ + if (oi->state == ISM_Waiting + && IPV4_ADDR_SAME(&nbr->address.u.prefix4, + &hello->bd_router)) + OSPF_ISM_EVENT_SCHEDULE(oi, ISM_BackupSeen); + + /* had not previously. */ + if ((IPV4_ADDR_SAME(&nbr->address.u.prefix4, &hello->d_router) + && IPV4_ADDR_CMP(&nbr->address.u.prefix4, &nbr->d_router)) + || (IPV4_ADDR_CMP(&nbr->address.u.prefix4, &hello->d_router) + && IPV4_ADDR_SAME(&nbr->address.u.prefix4, + &nbr->d_router))) + OSPF_ISM_EVENT_SCHEDULE(oi, ISM_NeighborChange); + + /* had not previously. */ + if ((IPV4_ADDR_SAME(&nbr->address.u.prefix4, &hello->bd_router) + && IPV4_ADDR_CMP(&nbr->address.u.prefix4, &nbr->bd_router)) + || (IPV4_ADDR_CMP(&nbr->address.u.prefix4, + &hello->bd_router) + && IPV4_ADDR_SAME(&nbr->address.u.prefix4, + &nbr->bd_router))) + OSPF_ISM_EVENT_SCHEDULE(oi, ISM_NeighborChange); + + /* Neighbor priority check. */ + if (nbr->priority >= 0 && nbr->priority != hello->priority) + OSPF_ISM_EVENT_SCHEDULE(oi, ISM_NeighborChange); + } + + /* Set neighbor information. */ + nbr->priority = hello->priority; + nbr->d_router = hello->d_router; + nbr->bd_router = hello->bd_router; + + /* + * RFC 3623 - Section 2: + * "If the restarting router determines that it was the Designated + * Router on a given segment prior to the restart, it elects + * itself as the Designated Router again. The restarting router + * knows that it was the Designated Router if, while the + * associated interface is in Waiting state, a Hello packet is + * received from a neighbor listing the router as the Designated + * Router". + */ + if (oi->area->ospf->gr_info.restart_in_progress + && oi->state == ISM_Waiting + && IPV4_ADDR_SAME(&hello->d_router, &oi->address->u.prefix4)) + DR(oi) = hello->d_router; +} + +/* Save DD flags/options/Seqnum received. */ +static void ospf_db_desc_save_current(struct ospf_neighbor *nbr, + struct ospf_db_desc *dd) +{ + nbr->last_recv.flags = dd->flags; + nbr->last_recv.options = dd->options; + nbr->last_recv.dd_seqnum = ntohl(dd->dd_seqnum); +} + +/* Process rest of DD packet. */ +static void ospf_db_desc_proc(struct stream *s, struct ospf_interface *oi, + struct ospf_neighbor *nbr, + struct ospf_db_desc *dd, uint16_t size) +{ + struct ospf_lsa *new, *find; + struct lsa_header *lsah; + + stream_forward_getp(s, OSPF_DB_DESC_MIN_SIZE); + for (size -= OSPF_DB_DESC_MIN_SIZE; size >= OSPF_LSA_HEADER_SIZE; + size -= OSPF_LSA_HEADER_SIZE) { + lsah = (struct lsa_header *)stream_pnt(s); + stream_forward_getp(s, OSPF_LSA_HEADER_SIZE); + + /* Unknown LS type. */ + if (lsah->type < OSPF_MIN_LSA || lsah->type >= OSPF_MAX_LSA) { + flog_warn(EC_OSPF_PACKET, + "Packet [DD:RECV]: Unknown LS type %d.", + lsah->type); + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_SeqNumberMismatch); + return; + } + + if (IS_OPAQUE_LSA(lsah->type) + && !CHECK_FLAG(nbr->options, OSPF_OPTION_O)) { + flog_warn(EC_OSPF_PACKET, + "LSA[Type%d:%pI4] from %pI4: Opaque capability mismatch?", + lsah->type, &lsah->id, &lsah->adv_router); + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_SeqNumberMismatch); + return; + } + + switch (lsah->type) { + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + /* Check for stub area. Reject if AS-External from stub + but + allow if from NSSA. */ + if (oi->area->external_routing == OSPF_AREA_STUB) { + flog_warn( + EC_OSPF_PACKET, + "Packet [DD:RECV]: LSA[Type%d:%pI4] from %s area.", + lsah->type, &lsah->id, + (oi->area->external_routing + == OSPF_AREA_STUB) + ? "STUB" + : "NSSA"); + OSPF_NSM_EVENT_SCHEDULE(nbr, + NSM_SeqNumberMismatch); + return; + } + break; + default: + break; + } + + /* Create LS-request object. */ + new = ospf_ls_request_new(lsah); + + /* Lookup received LSA, then add LS request list. */ + find = ospf_lsa_lookup_by_header(oi->area, lsah); + + /* ospf_lsa_more_recent is fine with NULL pointers */ + switch (ospf_lsa_more_recent(find, new)) { + case -1: + /* Neighbour has a more recent LSA, we must request it + */ + ospf_ls_request_add(nbr, new); + /* fallthru */ + case 0: + /* If we have a copy of this LSA, it's either less + * recent + * and we're requesting it from neighbour (the case + * above), or + * it's as recent and we both have same copy (this + * case). + * + * In neither of these two cases is there any point in + * describing our copy of the LSA to the neighbour in a + * DB-Summary packet, if we're still intending to do so. + * + * See: draft-ogier-ospf-dbex-opt-00.txt, describing the + * backward compatible optimisation to OSPF DB Exchange + * / + * DB Description process implemented here. + */ + if (find) + ospf_lsdb_delete(&nbr->db_sum, find); + ospf_lsa_discard(new); + break; + default: + /* We have the more recent copy, nothing specific to do: + * - no need to request neighbours stale copy + * - must leave DB summary list copy alone + */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Packet [DD:RECV]: LSA received Type %d, ID %pI4 is not recent.", + lsah->type, &lsah->id); + ospf_lsa_discard(new); + } + } + + /* Master */ + if (IS_SET_DD_MS(nbr->dd_flags)) { + nbr->dd_seqnum++; + + /* Both sides have no More, then we're done with Exchange */ + if (!IS_SET_DD_M(dd->flags) && !IS_SET_DD_M(nbr->dd_flags)) + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_ExchangeDone); + else + ospf_db_desc_send(nbr); + } + /* Slave */ + else { + nbr->dd_seqnum = ntohl(dd->dd_seqnum); + + /* Send DD packet in reply. + * + * Must be done to acknowledge the Master's DD, regardless of + * whether we have more LSAs ourselves to describe. + * + * This function will clear the 'More' bit, if after this DD + * we have no more LSAs to describe to the master.. + */ + ospf_db_desc_send(nbr); + + /* Slave can raise ExchangeDone now, if master is also done */ + if (!IS_SET_DD_M(dd->flags) && !IS_SET_DD_M(nbr->dd_flags)) + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_ExchangeDone); + } + + /* Save received neighbor values from DD. */ + ospf_db_desc_save_current(nbr, dd); + + if (!nbr->t_ls_req) + ospf_ls_req_send(nbr); +} + +static int ospf_db_desc_is_dup(struct ospf_db_desc *dd, + struct ospf_neighbor *nbr) +{ + /* Is DD duplicated? */ + if (dd->options == nbr->last_recv.options + && dd->flags == nbr->last_recv.flags + && dd->dd_seqnum == htonl(nbr->last_recv.dd_seqnum)) + return 1; + + return 0; +} + +/* OSPF Database Description message read -- RFC2328 Section 10.6. */ +static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh, + struct stream *s, struct ospf_interface *oi, + uint16_t size) +{ + struct ospf_db_desc *dd; + struct ospf_neighbor *nbr; + + /* Increment statistics. */ + oi->db_desc_in++; + + dd = (struct ospf_db_desc *)stream_pnt(s); + + nbr = ospf_nbr_lookup(oi, iph, ospfh); + if (nbr == NULL) { + flog_warn(EC_OSPF_PACKET, "Packet[DD]: Unknown Neighbor %pI4", + &ospfh->router_id); + return; + } + + /* Check MTU. */ + if ((OSPF_IF_PARAM(oi, mtu_ignore) == 0) + && (ntohs(dd->mtu) > oi->ifp->mtu)) { + flog_warn( + EC_OSPF_PACKET, + "Packet[DD]: Neighbor %pI4 MTU %u is larger than [%s]'s MTU %u", + &nbr->router_id, ntohs(dd->mtu), IF_NAME(oi), + oi->ifp->mtu); + return; + } + + /* + * XXX HACK by Hasso Tepper. Setting N/P bit in NSSA area DD packets is + * not + * required. In fact at least JunOS sends DD packets with P bit clear. + * Until proper solution is developped, this hack should help. + * + * Update: According to the RFCs, N bit is specified /only/ for Hello + * options, unfortunately its use in DD options is not specified. Hence + * some + * implementations follow E-bit semantics and set it in DD options, and + * some + * treat it as unspecified and hence follow the directive "default for + * options is clear", ie unset. + * + * Reset the flag, as ospfd follows E-bit semantics. + */ + if ((oi->area->external_routing == OSPF_AREA_NSSA) + && (CHECK_FLAG(nbr->options, OSPF_OPTION_NP)) + && (!CHECK_FLAG(dd->options, OSPF_OPTION_NP))) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Packet[DD]: Neighbour %pI4: Has NSSA capability, sends with N bit clear in DD options", + &nbr->router_id); + SET_FLAG(dd->options, OSPF_OPTION_NP); + } + +#ifdef REJECT_IF_TBIT_ON + if (CHECK_FLAG(dd->options, OSPF_OPTION_MT)) { + /* + * In Hello protocol, optional capability must have checked + * to prevent this T-bit enabled router be my neighbor. + */ + flog_warn(EC_OSPF_PACKET, "Packet[DD]: Neighbor %pI4: T-bit on?", + &nbr->router_id); + return; + } +#endif /* REJECT_IF_TBIT_ON */ + + if (CHECK_FLAG(dd->options, OSPF_OPTION_O) && + (!CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) || + !OSPF_IF_PARAM(oi, opaque_capable))) { + /* + * This node is not configured to handle O-bit, for now. + * Clear it to ignore unsupported capability proposed by + * neighbor. + */ + UNSET_FLAG(dd->options, OSPF_OPTION_O); + } + + if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) + zlog_info( + "%s:Packet[DD]: Neighbor %pI4 state is %s, seq_num:0x%x, local:0x%x", + ospf_get_name(oi->ospf), &nbr->router_id, + lookup_msg(ospf_nsm_state_msg, nbr->state, NULL), + ntohl(dd->dd_seqnum), nbr->dd_seqnum); + + /* Process DD packet by neighbor status. */ + switch (nbr->state) { + case NSM_Down: + case NSM_Attempt: + case NSM_TwoWay: + if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) + zlog_info( + "Packet[DD]: Neighbor %pI4 state is %s, packet discarded.", + &nbr->router_id, + lookup_msg(ospf_nsm_state_msg, nbr->state, + NULL)); + break; + case NSM_Init: + OSPF_NSM_EVENT_EXECUTE(nbr, NSM_TwoWayReceived); + /* If the new state is ExStart, the processing of the current + packet should then continue in this new state by falling + through to case ExStart below. */ + if (nbr->state != NSM_ExStart) + break; + /* fallthru */ + case NSM_ExStart: + /* Initial DBD */ + if ((IS_SET_DD_ALL(dd->flags) == OSPF_DD_FLAG_ALL) + && (size == OSPF_DB_DESC_MIN_SIZE)) { + if (IPV4_ADDR_CMP(&nbr->router_id, &oi->ospf->router_id) + > 0) { + /* We're Slave---obey */ + if (CHECK_FLAG(oi->ospf->config, + OSPF_LOG_ADJACENCY_DETAIL)) + zlog_info( + "Packet[DD]: Neighbor %pI4 Negotiation done (Slave).", + &nbr->router_id); + + nbr->dd_seqnum = ntohl(dd->dd_seqnum); + + /* Reset I/MS */ + UNSET_FLAG(nbr->dd_flags, + (OSPF_DD_FLAG_MS | OSPF_DD_FLAG_I)); + } else { + /* We're Master, ignore the initial DBD from + * Slave */ + if (CHECK_FLAG(oi->ospf->config, + OSPF_LOG_ADJACENCY_DETAIL)) + zlog_info( + "Packet[DD]: Neighbor %pI4: Initial DBD from Slave, ignoring.", + &nbr->router_id); + break; + } + } + /* Ack from the Slave */ + else if (!IS_SET_DD_MS(dd->flags) && !IS_SET_DD_I(dd->flags) + && ntohl(dd->dd_seqnum) == nbr->dd_seqnum + && IPV4_ADDR_CMP(&nbr->router_id, &oi->ospf->router_id) + < 0) { + zlog_info( + "Packet[DD]: Neighbor %pI4 Negotiation done (Master).", + &nbr->router_id); + /* Reset I, leaving MS */ + UNSET_FLAG(nbr->dd_flags, OSPF_DD_FLAG_I); + } else { + flog_warn(EC_OSPF_PACKET, + "Packet[DD]: Neighbor %pI4 Negotiation fails.", + &nbr->router_id); + break; + } + + /* This is where the real Options are saved */ + nbr->options = dd->options; + + if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) && + OSPF_IF_PARAM(oi, opaque_capable)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Neighbor[%pI4] is %sOpaque-capable.", + &nbr->router_id, + CHECK_FLAG(nbr->options, OSPF_OPTION_O) + ? "" + : "NOT "); + + if (!CHECK_FLAG(nbr->options, OSPF_OPTION_O) + && IPV4_ADDR_SAME(&DR(oi), + &nbr->address.u.prefix4)) { + flog_warn( + EC_OSPF_PACKET, + "DR-neighbor[%pI4] is NOT opaque-capable; Opaque-LSAs cannot be reliably advertised in this network.", + &nbr->router_id); + /* This situation is undesirable, but not a real + * error. */ + } + } + + OSPF_NSM_EVENT_EXECUTE(nbr, NSM_NegotiationDone); + + /* continue processing rest of packet. */ + ospf_db_desc_proc(s, oi, nbr, dd, size); + break; + case NSM_Exchange: + if (ospf_db_desc_is_dup(dd, nbr)) { + if (IS_SET_DD_MS(nbr->dd_flags)) + /* Master: discard duplicated DD packet. */ + zlog_info( + "Packet[DD] (Master): Neighbor %pI4 packet duplicated.", + &nbr->router_id); + else + /* Slave: cause to retransmit the last Database + Description. */ + { + zlog_info( + "Packet[DD] [Slave]: Neighbor %pI4 packet duplicated.", + &nbr->router_id); + ospf_db_desc_resend(nbr); + } + break; + } + + /* Otherwise DD packet should be checked. */ + /* Check Master/Slave bit mismatch */ + if (IS_SET_DD_MS(dd->flags) + != IS_SET_DD_MS(nbr->last_recv.flags)) { + flog_warn(EC_OSPF_PACKET, + "Packet[DD]: Neighbor %pI4 MS-bit mismatch.", + &nbr->router_id); + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_SeqNumberMismatch); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Packet[DD]: dd->flags=%d, nbr->dd_flags=%d", + dd->flags, nbr->dd_flags); + break; + } + + /* Check initialize bit is set. */ + if (IS_SET_DD_I(dd->flags)) { + zlog_info("Packet[DD]: Neighbor %pI4 I-bit set.", + &nbr->router_id); + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_SeqNumberMismatch); + break; + } + + /* Check DD Options. */ + if (dd->options != nbr->options) { + flog_warn(EC_OSPF_PACKET, + "Packet[DD]: Neighbor %pI4 options mismatch.", + &nbr->router_id); + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_SeqNumberMismatch); + break; + } + + /* Check DD sequence number. */ + if ((IS_SET_DD_MS(nbr->dd_flags) + && ntohl(dd->dd_seqnum) != nbr->dd_seqnum) + || (!IS_SET_DD_MS(nbr->dd_flags) + && ntohl(dd->dd_seqnum) != nbr->dd_seqnum + 1)) { + flog_warn( + EC_OSPF_PACKET, + "Packet[DD]: Neighbor %pI4 sequence number mismatch.", + &nbr->router_id); + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_SeqNumberMismatch); + break; + } + + /* Continue processing rest of packet. */ + ospf_db_desc_proc(s, oi, nbr, dd, size); + break; + case NSM_Loading: + case NSM_Full: + if (ospf_db_desc_is_dup(dd, nbr)) { + if (IS_SET_DD_MS(nbr->dd_flags)) { + /* Master should discard duplicate DD packet. */ + zlog_info( + "Packet[DD]: Neighbor %pI4 duplicated, packet discarded.", + &nbr->router_id); + break; + } else { + if (monotime_since(&nbr->last_send_ts, NULL) + < nbr->v_inactivity * 1000000LL) { + /* In states Loading and Full the slave + must resend + its last Database Description packet + in response to + duplicate Database Description + packets received + from the master. For this reason the + slave must + wait RouterDeadInterval seconds + before freeing the + last Database Description packet. + Reception of a + Database Description packet from the + master after + this interval will generate a + SeqNumberMismatch + neighbor event. RFC2328 Section 10.8 + */ + ospf_db_desc_resend(nbr); + break; + } + } + } + + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_SeqNumberMismatch); + break; + default: + flog_warn(EC_OSPF_PACKET, + "Packet[DD]: Neighbor %pI4 NSM illegal status %u.", + &nbr->router_id, nbr->state); + break; + } +} + +#define OSPF_LSA_KEY_SIZE 12 /* type(4) + id(4) + ar(4) */ + +/* OSPF Link State Request Read -- RFC2328 Section 10.7. */ +static void ospf_ls_req(struct ip *iph, struct ospf_header *ospfh, + struct stream *s, struct ospf_interface *oi, + uint16_t size) +{ + struct ospf_neighbor *nbr; + uint32_t ls_type; + struct in_addr ls_id; + struct in_addr adv_router; + struct ospf_lsa *find; + struct list *ls_upd; + unsigned int length; + + /* Increment statistics. */ + oi->ls_req_in++; + + nbr = ospf_nbr_lookup(oi, iph, ospfh); + if (nbr == NULL) { + flog_warn(EC_OSPF_PACKET, + "Link State Request: Unknown Neighbor %pI4", + &ospfh->router_id); + return; + } + + /* Neighbor State should be Exchange or later. */ + if (nbr->state != NSM_Exchange && nbr->state != NSM_Loading + && nbr->state != NSM_Full) { + flog_warn( + EC_OSPF_PACKET, + "Link State Request received from %pI4: Neighbor state is %s, packet discarded.", + &ospfh->router_id, + lookup_msg(ospf_nsm_state_msg, nbr->state, NULL)); + return; + } + + /* Send Link State Update for ALL requested LSAs. */ + ls_upd = list_new(); + length = OSPF_HEADER_SIZE + OSPF_LS_UPD_MIN_SIZE; + + while (size >= OSPF_LSA_KEY_SIZE) { + /* Get one slice of Link State Request. */ + ls_type = stream_getl(s); + ls_id.s_addr = stream_get_ipv4(s); + adv_router.s_addr = stream_get_ipv4(s); + + /* Verify LSA type. */ + if (ls_type < OSPF_MIN_LSA || ls_type >= OSPF_MAX_LSA) { + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_BadLSReq); + list_delete(&ls_upd); + return; + } + + /* Search proper LSA in LSDB. */ + find = ospf_lsa_lookup(oi->ospf, oi->area, ls_type, ls_id, + adv_router); + if (find == NULL) { + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_BadLSReq); + list_delete(&ls_upd); + return; + } + + /* Packet overflows MTU size, send immediately. */ + if (length + ntohs(find->data->length) > ospf_packet_max(oi)) { + if (oi->type == OSPF_IFTYPE_NBMA) + ospf_ls_upd_send(nbr, ls_upd, + OSPF_SEND_PACKET_DIRECT, 0); + else + ospf_ls_upd_send(nbr, ls_upd, + OSPF_SEND_PACKET_INDIRECT, 0); + + /* Only remove list contents. Keep ls_upd. */ + list_delete_all_node(ls_upd); + + length = OSPF_HEADER_SIZE + OSPF_LS_UPD_MIN_SIZE; + } + + /* Append LSA to update list. */ + listnode_add(ls_upd, find); + length += ntohs(find->data->length); + + size -= OSPF_LSA_KEY_SIZE; + } + + /* Send rest of Link State Update. */ + if (listcount(ls_upd) > 0) { + if (oi->type == OSPF_IFTYPE_NBMA) + ospf_ls_upd_send(nbr, ls_upd, OSPF_SEND_PACKET_DIRECT, + 0); + else + ospf_ls_upd_send(nbr, ls_upd, OSPF_SEND_PACKET_INDIRECT, + 0); + + list_delete(&ls_upd); + } else + list_delete(&ls_upd); +} + +/* Get the list of LSAs from Link State Update packet. + And process some validation -- RFC2328 Section 13. (1)-(2). */ +static struct list *ospf_ls_upd_list_lsa(struct ospf_neighbor *nbr, + struct stream *s, + struct ospf_interface *oi, size_t size) +{ + uint16_t count, sum; + uint32_t length; + struct lsa_header *lsah; + struct ospf_lsa *lsa; + struct list *lsas; + + lsas = list_new(); + + count = stream_getl(s); + size -= OSPF_LS_UPD_MIN_SIZE; /* # LSAs */ + + for (; size >= OSPF_LSA_HEADER_SIZE && count > 0; + size -= length, stream_forward_getp(s, length), count--) { + lsah = (struct lsa_header *)stream_pnt(s); + length = ntohs(lsah->length); + + if (length > size) { + flog_warn( + EC_OSPF_PACKET, + "Link State Update: LSA length exceeds packet size."); + break; + } + + if (length < OSPF_LSA_HEADER_SIZE) { + flog_warn(EC_OSPF_PACKET, + "Link State Update: LSA length too small."); + break; + } + + /* Validate the LSA's LS checksum. */ + sum = lsah->checksum; + if (!ospf_lsa_checksum_valid(lsah)) { + /* (bug #685) more details in a one-line message make it + * possible + * to identify problem source on the one hand and to + * have a better + * chance to compress repeated messages in syslog on the + * other */ + flog_warn( + EC_OSPF_PACKET, + "Link State Update: LSA checksum error %x/%x, ID=%pI4 from: nbr %pI4, router ID %pI4, adv router %pI4", + sum, lsah->checksum, &lsah->id, + &nbr->src, &nbr->router_id, + &lsah->adv_router); + continue; + } + + /* Examine the LSA's LS type. */ + if (lsah->type < OSPF_MIN_LSA || lsah->type >= OSPF_MAX_LSA) { + flog_warn(EC_OSPF_PACKET, + "Link State Update: Unknown LS type %d", + lsah->type); + continue; + } + + /* + * What if the received LSA's age is greater than MaxAge? + * Treat it as a MaxAge case -- endo. + */ + if (ntohs(lsah->ls_age) > OSPF_LSA_MAXAGE) + lsah->ls_age = htons(OSPF_LSA_MAXAGE); + + if (CHECK_FLAG(nbr->options, OSPF_OPTION_O)) { +#ifdef STRICT_OBIT_USAGE_CHECK + if ((IS_OPAQUE_LSA(lsah->type) + && !CHECK_FLAG(lsah->options, OSPF_OPTION_O)) + || (!IS_OPAQUE_LSA(lsah->type) + && CHECK_FLAG(lsah->options, OSPF_OPTION_O))) { + /* + * This neighbor must know the exact usage of + * O-bit; + * the bit will be set in Type-9,10,11 LSAs + * only. + */ + flog_warn(EC_OSPF_PACKET, + "LSA[Type%d:%pI4]: O-bit abuse?", + lsah->type, &lsah->id); + continue; + } +#endif /* STRICT_OBIT_USAGE_CHECK */ + + /* Do not take in AS External Opaque-LSAs if we are a + * stub. */ + if (lsah->type == OSPF_OPAQUE_AS_LSA + && nbr->oi->area->external_routing + != OSPF_AREA_DEFAULT) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type%d:%pI4]: We are a stub, don't take this LSA.", + lsah->type, + &lsah->id); + continue; + } + } else if (IS_OPAQUE_LSA(lsah->type)) { + flog_warn( + EC_OSPF_PACKET, + "LSA[Type%d:%pI4] from %pI4: Opaque capability mismatch?", + lsah->type, &lsah->id, &lsah->adv_router); + continue; + } + + /* Create OSPF LSA instance. */ + lsa = ospf_lsa_new_and_data(length); + + lsa->vrf_id = oi->ospf->vrf_id; + /* We may wish to put some error checking if type NSSA comes in + and area not in NSSA mode */ + switch (lsah->type) { + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + lsa->area = NULL; + break; + case OSPF_OPAQUE_LINK_LSA: + lsa->oi = oi; /* Remember incoming interface for + flooding control. */ + /* Fallthrough */ + default: + lsa->area = oi->area; + break; + } + + memcpy(lsa->data, lsah, length); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type%d:%pI4]: %p new LSA created with Link State Update", + lsa->data->type, &lsa->data->id, + (void *)lsa); + listnode_add(lsas, lsa); + } + + return lsas; +} + +/* Cleanup Update list. */ +static void ospf_upd_list_clean(struct list *lsas) +{ + struct listnode *node, *nnode; + struct ospf_lsa *lsa; + + for (ALL_LIST_ELEMENTS(lsas, node, nnode, lsa)) + ospf_lsa_discard(lsa); + + list_delete(&lsas); +} + +/* OSPF Link State Update message read -- RFC2328 Section 13. */ +static void ospf_ls_upd(struct ospf *ospf, struct ip *iph, + struct ospf_header *ospfh, struct stream *s, + struct ospf_interface *oi, uint16_t size) +{ + struct ospf_neighbor *nbr; + struct list *lsas; + struct listnode *node, *nnode; + struct ospf_lsa *lsa = NULL; + /* unsigned long ls_req_found = 0; */ + + /* Dis-assemble the stream, update each entry, re-encapsulate for + * flooding */ + + /* Increment statistics. */ + oi->ls_upd_in++; + + /* Check neighbor. */ + nbr = ospf_nbr_lookup(oi, iph, ospfh); + if (nbr == NULL) { + flog_warn(EC_OSPF_PACKET, + "Link State Update: Unknown Neighbor %pI4 on int: %s", + &ospfh->router_id, IF_NAME(oi)); + return; + } + + /* Check neighbor state. */ + if (nbr->state < NSM_Exchange) { + if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) + zlog_debug( + "Link State Update: Neighbor[%pI4] state %s is less than Exchange", + &ospfh->router_id, + lookup_msg(ospf_nsm_state_msg, nbr->state, + NULL)); + return; + } + + /* 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); + + if (lsas == NULL) + return; +#define DISCARD_LSA(L, N) \ + { \ + if (IS_DEBUG_OSPF_EVENT) \ + zlog_debug( \ + "ospf_lsa_discard() in ospf_ls_upd() point %d: lsa %p" \ + " Type-%d", \ + N, (void *)lsa, (int)lsa->data->type); \ + ospf_lsa_discard(L); \ + continue; \ + } + + /* Process each LSA received in the one packet. + * + * Numbers in parentheses, e.g. (1), (2), etc., and the corresponding + * text below are from the steps in RFC 2328, Section 13. + */ + for (ALL_LIST_ELEMENTS(lsas, node, nnode, lsa)) { + struct ospf_lsa *ls_ret, *current; + int ret = 1; + + 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); + + listnode_delete(lsas, + lsa); /* We don't need it in list anymore */ + + /* (1) Validate Checksum - Done above by ospf_ls_upd_list_lsa() + */ + + /* (2) LSA Type - Done above by ospf_ls_upd_list_lsa() */ + + /* (3) Do not take in AS External LSAs if we are a stub or NSSA. + */ + + /* Do not take in AS NSSA if this neighbor and we are not NSSA + */ + + /* Do take in Type-7's if we are an NSSA */ + + /* If we are also an ABR, later translate them to a Type-5 + * packet */ + + /* Later, an NSSA Re-fresh can Re-fresh Type-7's and an ABR will + translate them to a separate Type-5 packet. */ + + if (lsa->data->type == OSPF_AS_EXTERNAL_LSA) + /* Reject from STUB or NSSA */ + if (nbr->oi->area->external_routing + != OSPF_AREA_DEFAULT) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "Incoming External LSA Discarded: We are NSSA/STUB Area"); + DISCARD_LSA(lsa, 1); + } + + if (lsa->data->type == OSPF_AS_NSSA_LSA) + if (nbr->oi->area->external_routing != OSPF_AREA_NSSA) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "Incoming NSSA LSA Discarded: Not NSSA Area"); + DISCARD_LSA(lsa, 2); + } + + /* VU229804: Router-LSA Adv-ID must be equal to LS-ID */ + if (lsa->data->type == OSPF_ROUTER_LSA) + if (!IPV4_ADDR_SAME(&lsa->data->id, + &lsa->data->adv_router)) { + flog_err( + EC_OSPF_ROUTER_LSA_MISMATCH, + "Incoming Router-LSA from %pI4 with Adv-ID[%pI4] != LS-ID[%pI4]", + &ospfh->router_id, &lsa->data->id, + &lsa->data->adv_router); + flog_err( + EC_OSPF_DOMAIN_CORRUPT, + "OSPF domain compromised by attack or corruption. Verify correct operation of -ALL- OSPF routers."); + DISCARD_LSA(lsa, 0); + } + + /* Find the LSA in the current database. */ + + current = ospf_lsa_lookup_by_header(oi->area, lsa->data); + + /* (4) If the LSA's LS age is equal to MaxAge, and there is + currently + no instance of the LSA in the router's link state database, + and none of router's neighbors are in states Exchange or + Loading, + then take the following actions: */ + + if (IS_LSA_MAXAGE(lsa) && !current + && ospf_check_nbr_status(oi->ospf)) { + /* (4a) Response Link State Acknowledgment. */ + ospf_ls_ack_send(nbr, lsa); + + /* (4b) Discard LSA. */ + if (IS_DEBUG_OSPF(lsa, LSA)) { + zlog_debug( + "Link State Update[%s]: LS age is equal to MaxAge.", + dump_lsa_key(lsa)); + } + DISCARD_LSA(lsa, 3); + } + + if (IS_OPAQUE_LSA(lsa->data->type) + && IPV4_ADDR_SAME(&lsa->data->adv_router, + &oi->ospf->router_id)) { + /* + * Even if initial flushing seems to be completed, there + * might + * be a case that self-originated LSA with MaxAge still + * remain + * in the routing domain. + * Just send an LSAck message to cease retransmission. + */ + if (IS_LSA_MAXAGE(lsa)) { + zlog_info("LSA[%s]: Boomerang effect?", + dump_lsa_key(lsa)); + ospf_ls_ack_send(nbr, lsa); + ospf_lsa_discard(lsa); + + if (current != NULL && !IS_LSA_MAXAGE(current)) + ospf_opaque_lsa_refresh_schedule( + current); + continue; + } + + /* + * If an instance of self-originated Opaque-LSA is not + * found + * in the LSDB, there are some possible cases here. + * + * 1) This node lost opaque-capability after restart. + * 2) Else, a part of opaque-type is no more supported. + * 3) Else, a part of opaque-id is no more supported. + * + * Anyway, it is still this node's responsibility to + * flush it. + * Otherwise, the LSA instance remains in the routing + * domain + * until its age reaches to MaxAge. + */ + /* XXX: We should deal with this for *ALL* LSAs, not + * just opaque */ + if (current == NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[%s]: Previously originated Opaque-LSA, not found in the LSDB.", + dump_lsa_key(lsa)); + + SET_FLAG(lsa->flags, OSPF_LSA_SELF); + + ospf_ls_ack_send(nbr, lsa); + + if (!ospf->gr_info.restart_in_progress) { + ospf_opaque_self_originated_lsa_received( + nbr, lsa); + continue; + } + } + } + + /* It might be happen that received LSA is self-originated + * network LSA, but + * router ID is changed. So, we should check if LSA is a + * network-LSA whose + * Link State ID is one of the router's own IP interface + * addresses but whose + * Advertising Router is not equal to the router's own Router ID + * According to RFC 2328 12.4.2 and 13.4 this LSA should be + * flushed. + */ + + if (lsa->data->type == OSPF_NETWORK_LSA) { + struct listnode *oinode, *oinnode; + struct ospf_interface *out_if; + int Flag = 0; + + for (ALL_LIST_ELEMENTS(oi->ospf->oiflist, oinode, + oinnode, out_if)) { + if (out_if == NULL) + break; + + if ((IPV4_ADDR_SAME(&out_if->address->u.prefix4, + &lsa->data->id)) + && (!(IPV4_ADDR_SAME( + &oi->ospf->router_id, + &lsa->data->adv_router)))) { + if (out_if->network_lsa_self) { + ospf_lsa_flush_area( + lsa, out_if->area); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "ospf_lsa_discard() in ospf_ls_upd() point 9: lsa %p Type-%d", + (void *)lsa, + (int)lsa->data + ->type); + ospf_lsa_discard(lsa); + Flag = 1; + } + break; + } + } + if (Flag) + continue; + } + + /* (5) Find the instance of this LSA that is currently contained + in the router's link state database. If there is no + database copy, or the received LSA is more recent than + the database copy the following steps must be performed. + (The sub steps from RFC 2328 section 13 step (5) will be + performed in + ospf_flood() ) */ + + if (current == NULL + || (ret = ospf_lsa_more_recent(current, lsa)) < 0) { + /* CVE-2017-3224 */ + if (current && (IS_LSA_MAX_SEQ(current)) + && (IS_LSA_MAX_SEQ(lsa)) && !IS_LSA_MAXAGE(lsa)) { + zlog_debug( + "Link State Update[%s]: has Max Seq and higher checksum but not MaxAge. Dropping it", + dump_lsa_key(lsa)); + + DISCARD_LSA(lsa, 4); + } + + /* Actual flooding procedure. */ + if (ospf_flood(oi->ospf, nbr, current, lsa) + < 0) /* Trap NSSA later. */ + DISCARD_LSA(lsa, 5); + + /* GR: check for network topology change. */ + if (ospf->gr_info.restart_in_progress && + ((lsa->data->type == OSPF_ROUTER_LSA || + lsa->data->type == OSPF_NETWORK_LSA))) + ospf_gr_check_lsdb_consistency(oi->ospf, + oi->area); + + continue; + } + + /* (6) Else, If there is an instance of the LSA on the sending + neighbor's Link state request list, an error has occurred in + the Database Exchange process. In this case, restart the + Database Exchange process by generating the neighbor event + BadLSReq for the sending neighbor and stop processing the + Link State Update packet. */ + + if (ospf_ls_request_lookup(nbr, lsa)) { + OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_BadLSReq); + flog_warn( + EC_OSPF_PACKET, + "LSA[%s] instance exists on Link state request list", + dump_lsa_key(lsa)); + + /* Clean list of LSAs. */ + ospf_upd_list_clean(lsas); + /* this lsa is not on lsas list already. */ + ospf_lsa_discard(lsa); + return; + } + + /* If the received LSA is the same instance as the database copy + (i.e., neither one is more recent) the following two steps + should be performed: */ + + if (ret == 0) { + /* If the LSA is listed in the Link state retransmission + list + for the receiving adjacency, the router itself is + expecting + an acknowledgment for this LSA. The router should + treat the + received LSA as an acknowledgment by removing the LSA + from + the Link state retransmission list. This is termed + an + "implied acknowledgment". */ + + ls_ret = ospf_ls_retransmit_lookup(nbr, lsa); + + if (ls_ret != NULL) { + ospf_ls_retransmit_delete(nbr, ls_ret); + + /* Delayed acknowledgment sent if advertisement + received + from Designated Router, otherwise do nothing. + */ + if (oi->state == ISM_Backup) + if (NBR_IS_DR(nbr)) + listnode_add( + oi->ls_ack, + ospf_lsa_lock(lsa)); + + DISCARD_LSA(lsa, 6); + } else + /* Acknowledge the receipt of the LSA by sending a + Link State Acknowledgment packet back out the + receiving + interface. */ + { + ospf_ls_ack_send(nbr, lsa); + DISCARD_LSA(lsa, 7); + } + } + + /* The database copy is more recent. If the database copy + has LS age equal to MaxAge and LS sequence number equal to + MaxSequenceNumber, simply discard the received LSA without + acknowledging it. (In this case, the LSA's LS sequence number + is + wrapping, and the MaxSequenceNumber LSA must be completely + flushed before any new LSA instance can be introduced). */ + + else if (ret > 0) /* Database copy is more recent */ + { + if (IS_LSA_MAXAGE(current) + && current->data->ls_seqnum + == htonl(OSPF_MAX_SEQUENCE_NUMBER)) { + DISCARD_LSA(lsa, 8); + } + /* Otherwise, as long as the database copy has not been + sent in a + Link State Update within the last MinLSArrival + seconds, send the + database copy back to the sending neighbor, + encapsulated within + a Link State Update Packet. The Link State Update + Packet should + be sent directly to the neighbor. In so doing, do not + put the + database copy of the LSA on the neighbor's link state + retransmission list, and do not acknowledge the + received (less + recent) LSA instance. */ + else { + if (monotime_since(¤t->tv_orig, NULL) + >= ospf->min_ls_arrival * 1000LL) + /* Trap NSSA type later.*/ + ospf_ls_upd_send_lsa( + nbr, current, + OSPF_SEND_PACKET_DIRECT); + DISCARD_LSA(lsa, 9); + } + } + } +#undef DISCARD_LSA + + assert(listcount(lsas) == 0); + list_delete(&lsas); +} + +/* OSPF Link State Acknowledgment message read -- RFC2328 Section 13.7. */ +static void ospf_ls_ack(struct ip *iph, struct ospf_header *ospfh, + struct stream *s, struct ospf_interface *oi, + uint16_t size) +{ + struct ospf_neighbor *nbr; + + /* increment statistics. */ + oi->ls_ack_in++; + + nbr = ospf_nbr_lookup(oi, iph, ospfh); + if (nbr == NULL) { + flog_warn(EC_OSPF_PACKET, + "Link State Acknowledgment: Unknown Neighbor %pI4", + &ospfh->router_id); + return; + } + + if (nbr->state < NSM_Exchange) { + if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) + zlog_debug( + "Link State Acknowledgment: Neighbor[%pI4] state %s is less than Exchange", + &ospfh->router_id, + lookup_msg(ospf_nsm_state_msg, nbr->state, + NULL)); + return; + } + + while (size >= OSPF_LSA_HEADER_SIZE) { + struct ospf_lsa *lsa, *lsr; + + lsa = ospf_lsa_new(); + lsa->data = (struct lsa_header *)stream_pnt(s); + lsa->vrf_id = oi->ospf->vrf_id; + + /* lsah = (struct lsa_header *) stream_pnt (s); */ + size -= OSPF_LSA_HEADER_SIZE; + stream_forward_getp(s, OSPF_LSA_HEADER_SIZE); + + if (lsa->data->type < OSPF_MIN_LSA + || lsa->data->type >= OSPF_MAX_LSA) { + lsa->data = NULL; + ospf_lsa_discard(lsa); + continue; + } + + lsr = ospf_ls_retransmit_lookup(nbr, lsa); + + if (lsr != NULL && ospf_lsa_more_recent(lsr, lsa) == 0) { + ospf_ls_retransmit_delete(nbr, lsr); + ospf_check_and_gen_init_seq_lsa(oi, lsa); + } + + lsa->data = NULL; + ospf_lsa_discard(lsa); + } + + return; +} + +static struct stream *ospf_recv_packet(struct ospf *ospf, int fd, + struct interface **ifp, + struct stream *ibuf) +{ + int ret; + struct ip *iph; + uint16_t ip_len; + ifindex_t ifindex = 0; + struct iovec iov; + /* Header and data both require alignment. */ + char buff[CMSG_SPACE(SOPT_SIZE_CMSG_IFINDEX_IPV4())]; + struct msghdr msgh; + + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_control = (caddr_t)buff; + msgh.msg_controllen = sizeof(buff); + + ret = stream_recvmsg(ibuf, fd, &msgh, MSG_DONTWAIT, + OSPF_MAX_PACKET_SIZE + 1); + if (ret < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) + flog_warn(EC_OSPF_PACKET, "stream_recvmsg failed: %s", + safe_strerror(errno)); + return NULL; + } + if ((unsigned int)ret < sizeof(struct ip)) { + flog_warn( + EC_OSPF_PACKET, + "%s: discarding runt packet of length %d (ip header size is %u)", + __func__, ret, (unsigned int)sizeof(iph)); + return NULL; + } + + /* Note that there should not be alignment problems with this assignment + because this is at the beginning of the stream data buffer. */ + iph = (struct ip *)STREAM_DATA(ibuf); + sockopt_iphdrincl_swab_systoh(iph); + + ip_len = iph->ip_len; + +#if defined(__FreeBSD__) && (__FreeBSD_version < 1000000) + /* + * Kernel network code touches incoming IP header parameters, + * before protocol specific processing. + * + * 1) Convert byteorder to host representation. + * --> ip_len, ip_id, ip_off + * + * 2) Adjust ip_len to strip IP header size! + * --> If user process receives entire IP packet via RAW + * socket, it must consider adding IP header size to + * the "ip_len" field of "ip" structure. + * + * For more details, see . + */ + ip_len = ip_len + (iph->ip_hl << 2); +#endif + +#if defined(__DragonFly__) + /* + * in DragonFly's raw socket, ip_len/ip_off are read + * in network byte order. + * As OpenBSD < 200311 adjust ip_len to strip IP header size! + */ + ip_len = ntohs(iph->ip_len) + (iph->ip_hl << 2); +#endif + + ifindex = getsockopt_ifindex(AF_INET, &msgh); + + *ifp = if_lookup_by_index(ifindex, ospf->vrf_id); + + if (ret != ip_len) { + flog_warn( + EC_OSPF_PACKET, + "%s read length mismatch: ip_len is %d, but recvmsg returned %d", + __func__, ip_len, ret); + return NULL; + } + + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug("%s: fd %d(%s) on interface %d(%s)", __func__, fd, + ospf_get_name(ospf), ifindex, + *ifp ? (*ifp)->name : "Unknown"); + return ibuf; +} + +static struct ospf_interface * +ospf_associate_packet_vl(struct ospf *ospf, struct interface *ifp, + struct ip *iph, struct ospf_header *ospfh) +{ + struct ospf_interface *rcv_oi; + struct ospf_vl_data *vl_data; + struct ospf_area *vl_area; + struct listnode *node; + + if (IN_MULTICAST(ntohl(iph->ip_dst.s_addr)) + || !OSPF_IS_AREA_BACKBONE(ospfh)) + return NULL; + + /* look for local OSPF interface matching the destination + * to determine Area ID. We presume therefore the destination address + * is unique, or at least (for "unnumbered" links), not used in other + * areas + */ + if ((rcv_oi = ospf_if_lookup_by_local_addr(ospf, NULL, iph->ip_dst)) + == NULL) + return NULL; + + for (ALL_LIST_ELEMENTS_RO(ospf->vlinks, node, vl_data)) { + vl_area = + ospf_area_lookup_by_area_id(ospf, vl_data->vl_area_id); + if (!vl_area) + continue; + + if (OSPF_AREA_SAME(&vl_area, &rcv_oi->area) + && IPV4_ADDR_SAME(&vl_data->vl_peer, &ospfh->router_id)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("associating packet with %s", + IF_NAME(vl_data->vl_oi)); + if (!CHECK_FLAG(vl_data->vl_oi->ifp->flags, IFF_UP)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "This VL is not up yet, sorry"); + return NULL; + } + + return vl_data->vl_oi; + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("couldn't find any VL to associate the packet with"); + + return NULL; +} + +static int ospf_check_area_id(struct ospf_interface *oi, + struct ospf_header *ospfh) +{ + /* Check match the Area ID of the receiving interface. */ + if (OSPF_AREA_SAME(&oi->area, &ospfh)) + return 1; + + return 0; +} + +/* Unbound socket will accept any Raw IP packets if proto is matched. + To prevent it, compare src IP address and i/f address with masking + i/f network mask. */ +static int ospf_check_network_mask(struct ospf_interface *oi, + struct in_addr ip_src) +{ + struct in_addr mask, me, him; + + if (oi->type == OSPF_IFTYPE_POINTOPOINT + || oi->type == OSPF_IFTYPE_VIRTUALLINK) + return 1; + + /* Ignore mask check for max prefix length (32) */ + if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT + && oi->address->prefixlen == IPV4_MAX_BITLEN) + return 1; + + masklen2ip(oi->address->prefixlen, &mask); + + me.s_addr = oi->address->u.prefix4.s_addr & mask.s_addr; + him.s_addr = ip_src.s_addr & mask.s_addr; + + if (IPV4_ADDR_SAME(&me, &him)) + return 1; + + return 0; +} + +/* Verify, that given link/TOS records are properly sized/aligned and match + Router-LSA "# links" and "# TOS" fields as specified in RFC2328 A.4.2. */ +static unsigned ospf_router_lsa_links_examin(struct router_lsa_link *link, + uint16_t linkbytes, + const uint16_t num_links) +{ + unsigned counted_links = 0, thislinklen; + + while (linkbytes >= OSPF_ROUTER_LSA_LINK_SIZE) { + thislinklen = + OSPF_ROUTER_LSA_LINK_SIZE + 4 * link->m[0].tos_count; + if (thislinklen > linkbytes) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug("%s: length error in link block #%u", + __func__, counted_links); + return MSG_NG; + } + link = (struct router_lsa_link *)((caddr_t)link + thislinklen); + linkbytes -= thislinklen; + counted_links++; + } + if (counted_links != num_links) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug("%s: %u link blocks declared, %u present", + __func__, num_links, counted_links); + return MSG_NG; + } + return MSG_OK; +} + +/* Verify, that the given LSA is properly sized/aligned (including type-specific + minimum length constraint). */ +static unsigned ospf_lsa_examin(struct lsa_header *lsah, const uint16_t lsalen, + const uint8_t headeronly) +{ + unsigned ret; + struct router_lsa *rlsa; + if (lsah->type < OSPF_MAX_LSA && ospf_lsa_minlen[lsah->type] + && lsalen < OSPF_LSA_HEADER_SIZE + ospf_lsa_minlen[lsah->type]) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug("%s: undersized (%u B) %s", __func__, lsalen, + lookup_msg(ospf_lsa_type_msg, lsah->type, + NULL)); + return MSG_NG; + } + switch (lsah->type) { + case OSPF_ROUTER_LSA: { + /* + * RFC2328 A.4.2, LSA header + 4 bytes followed by N>=0 + * (12+)-byte link blocks + */ + size_t linkbytes_len = lsalen - OSPF_LSA_HEADER_SIZE + - OSPF_ROUTER_LSA_MIN_SIZE; + + /* + * LSA link blocks are variable length but always multiples of + * 4; basic sanity check + */ + if (linkbytes_len % 4 != 0) + return MSG_NG; + + if (headeronly) + return MSG_OK; + + rlsa = (struct router_lsa *)lsah; + + ret = ospf_router_lsa_links_examin( + (struct router_lsa_link *)rlsa->link, + linkbytes_len, + ntohs(rlsa->links)); + break; + } + case OSPF_AS_EXTERNAL_LSA: + /* RFC2328 A.4.5, LSA header + 4 bytes followed by N>=1 12-bytes long + * blocks */ + case OSPF_AS_NSSA_LSA: + /* RFC3101 C, idem */ + ret = (lsalen - OSPF_LSA_HEADER_SIZE + - OSPF_AS_EXTERNAL_LSA_MIN_SIZE) + % 12 + ? MSG_NG + : MSG_OK; + break; + /* Following LSA types are considered OK length-wise as soon as their + * minimum + * length constraint is met and length of the whole LSA is a multiple of + * 4 + * (basic LSA header size is already a multiple of 4). */ + case OSPF_NETWORK_LSA: + /* RFC2328 A.4.3, LSA header + 4 bytes followed by N>=1 router-IDs */ + case OSPF_SUMMARY_LSA: + case OSPF_ASBR_SUMMARY_LSA: + /* RFC2328 A.4.4, LSA header + 4 bytes followed by N>=1 4-bytes TOS + * blocks */ + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + /* RFC5250 A.2, "some number of octets (of application-specific + * data) padded to 32-bit alignment." This is considered + * equivalent + * to 4-byte alignment of all other LSA types, see + * OSPF-ALIGNMENT.txt + * file for the detailed analysis of this passage. */ + ret = lsalen % 4 ? MSG_NG : MSG_OK; + break; + default: + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug("%s: unsupported LSA type 0x%02x", __func__, + lsah->type); + return MSG_NG; + } + if (ret != MSG_OK && IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug("%s: alignment error in %s", __func__, + lookup_msg(ospf_lsa_type_msg, lsah->type, NULL)); + return ret; +} + +/* Verify if the provided input buffer is a valid sequence of LSAs. This + includes verification of LSA blocks length/alignment and dispatching + of deeper-level checks. */ +static unsigned +ospf_lsaseq_examin(struct lsa_header *lsah, /* start of buffered data */ + size_t length, const uint8_t headeronly, + /* When declared_num_lsas is not 0, compare it to the real + number of LSAs + and treat the difference as an error. */ + const uint32_t declared_num_lsas) +{ + uint32_t counted_lsas = 0; + + while (length) { + uint16_t lsalen; + if (length < OSPF_LSA_HEADER_SIZE) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug( + "%s: undersized (%zu B) trailing (#%u) LSA header", + __func__, length, counted_lsas); + return MSG_NG; + } + /* save on ntohs() calls here and in the LSA validator */ + lsalen = ntohs(lsah->length); + if (lsalen < OSPF_LSA_HEADER_SIZE) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug( + "%s: malformed LSA header #%u, declared length is %u B", + __func__, counted_lsas, lsalen); + return MSG_NG; + } + if (headeronly) { + /* less checks here and in ospf_lsa_examin() */ + if (MSG_OK != ospf_lsa_examin(lsah, lsalen, 1)) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug( + "%s: malformed header-only LSA #%u", + __func__, counted_lsas); + return MSG_NG; + } + lsah = (struct lsa_header *)((caddr_t)lsah + + OSPF_LSA_HEADER_SIZE); + length -= OSPF_LSA_HEADER_SIZE; + } else { + /* make sure the input buffer is deep enough before + * further checks */ + if (lsalen > length) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug( + "%s: anomaly in LSA #%u: declared length is %u B, buffered length is %zu B", + __func__, counted_lsas, lsalen, + length); + return MSG_NG; + } + if (MSG_OK != ospf_lsa_examin(lsah, lsalen, 0)) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug("%s: malformed LSA #%u", + __func__, counted_lsas); + return MSG_NG; + } + lsah = (struct lsa_header *)((caddr_t)lsah + lsalen); + length -= lsalen; + } + counted_lsas++; + } + + if (declared_num_lsas && counted_lsas != declared_num_lsas) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug( + "%s: #LSAs declared (%u) does not match actual (%u)", + __func__, declared_num_lsas, counted_lsas); + return MSG_NG; + } + return MSG_OK; +} + +/* Verify a complete OSPF packet for proper sizing/alignment. */ +static unsigned ospf_packet_examin(struct ospf_header *oh, + const unsigned bytesonwire) +{ + uint16_t bytesdeclared, bytesauth; + unsigned ret; + struct ospf_ls_update *lsupd; + + /* Length, 1st approximation. */ + if (bytesonwire < OSPF_HEADER_SIZE) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug("%s: undersized (%u B) packet", __func__, + bytesonwire); + return MSG_NG; + } + /* Now it is safe to access header fields. Performing length check, + * allow + * for possible extra bytes of crypto auth/padding, which are not + * counted + * in the OSPF header "length" field. */ + if (oh->version != OSPF_VERSION) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug("%s: invalid (%u) protocol version", + __func__, oh->version); + return MSG_NG; + } + bytesdeclared = ntohs(oh->length); + if (ntohs(oh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC) + bytesauth = 0; + else { + if (oh->u.crypt.auth_data_len > KEYCHAIN_MAX_HASH_SIZE) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug( + "%s: unsupported crypto auth length (%u B)", + __func__, oh->u.crypt.auth_data_len); + return MSG_NG; + } + bytesauth = oh->u.crypt.auth_data_len; + } + if (bytesdeclared + bytesauth > bytesonwire) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug( + "%s: packet length error (%u real, %u+%u declared)", + __func__, bytesonwire, bytesdeclared, + bytesauth); + return MSG_NG; + } + /* Length, 2nd approximation. The type-specific constraint is checked + against declared length, not amount of bytes on wire. */ + if (oh->type >= OSPF_MSG_HELLO && oh->type <= OSPF_MSG_LS_ACK + && bytesdeclared + < OSPF_HEADER_SIZE + ospf_packet_minlen[oh->type]) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug("%s: undersized (%u B) %s packet", __func__, + bytesdeclared, + lookup_msg(ospf_packet_type_str, oh->type, + NULL)); + return MSG_NG; + } + switch (oh->type) { + case OSPF_MSG_HELLO: + /* RFC2328 A.3.2, packet header + OSPF_HELLO_MIN_SIZE bytes + followed + by N>=0 router-IDs. */ + ret = (bytesdeclared - OSPF_HEADER_SIZE - OSPF_HELLO_MIN_SIZE) + % 4 + ? MSG_NG + : MSG_OK; + break; + case OSPF_MSG_DB_DESC: + /* RFC2328 A.3.3, packet header + OSPF_DB_DESC_MIN_SIZE bytes + followed + by N>=0 header-only LSAs. */ + ret = ospf_lsaseq_examin( + (struct lsa_header *)((caddr_t)oh + OSPF_HEADER_SIZE + + OSPF_DB_DESC_MIN_SIZE), + bytesdeclared - OSPF_HEADER_SIZE + - OSPF_DB_DESC_MIN_SIZE, + 1, /* header-only LSAs */ + 0); + break; + case OSPF_MSG_LS_REQ: + /* RFC2328 A.3.4, packet header followed by N>=0 12-bytes + * request blocks. */ + ret = (bytesdeclared - OSPF_HEADER_SIZE - OSPF_LS_REQ_MIN_SIZE) + % OSPF_LSA_KEY_SIZE + ? MSG_NG + : MSG_OK; + break; + case OSPF_MSG_LS_UPD: + /* RFC2328 A.3.5, packet header + OSPF_LS_UPD_MIN_SIZE bytes + followed + by N>=0 full LSAs (with N declared beforehand). */ + lsupd = (struct ospf_ls_update *)((caddr_t)oh + + OSPF_HEADER_SIZE); + ret = ospf_lsaseq_examin( + (struct lsa_header *)((caddr_t)lsupd + + OSPF_LS_UPD_MIN_SIZE), + bytesdeclared - OSPF_HEADER_SIZE - OSPF_LS_UPD_MIN_SIZE, + 0, /* full LSAs */ + ntohl(lsupd->num_lsas) /* 32 bits */ + ); + break; + case OSPF_MSG_LS_ACK: + /* RFC2328 A.3.6, packet header followed by N>=0 header-only + * LSAs. */ + ret = ospf_lsaseq_examin( + (struct lsa_header *)((caddr_t)oh + OSPF_HEADER_SIZE + + OSPF_LS_ACK_MIN_SIZE), + bytesdeclared - OSPF_HEADER_SIZE - OSPF_LS_ACK_MIN_SIZE, + 1, /* header-only LSAs */ + 0); + break; + default: + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug("%s: invalid packet type 0x%02x", __func__, + oh->type); + return MSG_NG; + } + if (ret != MSG_OK && IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug("%s: malformed %s packet", __func__, + lookup_msg(ospf_packet_type_str, oh->type, NULL)); + return ret; +} + +/* OSPF Header verification. */ +static int ospf_verify_header(struct stream *ibuf, struct ospf_interface *oi, + struct ip *iph, struct ospf_header *ospfh) +{ + /* Check Area ID. */ + if (!ospf_check_area_id(oi, ospfh)) { + flog_warn(EC_OSPF_PACKET, + "interface %s: ospf_read invalid Area ID %pI4", + IF_NAME(oi), &ospfh->area_id); + return -1; + } + + /* Check network mask, Silently discarded. */ + if (!ospf_check_network_mask(oi, iph->ip_src)) { + flog_warn( + EC_OSPF_PACKET, + "interface %s: ospf_read network address is not same [%pI4]", + IF_NAME(oi), &iph->ip_src); + return -1; + } + + /* Check authentication. The function handles logging actions, where + * required. */ + if (!ospf_auth_check(oi, iph, ospfh)) + return -1; + + return 0; +} + +enum ospf_read_return_enum { + OSPF_READ_ERROR, + OSPF_READ_CONTINUE, +}; + +static enum ospf_read_return_enum ospf_read_helper(struct ospf *ospf) +{ + int ret; + struct stream *ibuf; + struct ospf_interface *oi; + struct ip *iph; + struct ospf_header *ospfh; + uint16_t length; + struct connected *c; + struct interface *ifp = NULL; + + stream_reset(ospf->ibuf); + ibuf = ospf_recv_packet(ospf, ospf->fd, &ifp, ospf->ibuf); + if (ibuf == NULL) + return OSPF_READ_ERROR; + + /* + * This raw packet is known to be at least as big as its + * IP header. Note that there should not be alignment problems with + * this assignment because this is at the beginning of the + * stream data buffer. + */ + iph = (struct ip *)STREAM_DATA(ibuf); + /* + * Note that sockopt_iphdrincl_swab_systoh was called in + * ospf_recv_packet. + */ + if (ifp == NULL) { + /* + * Handle cases where the platform does not support + * retrieving the ifindex, and also platforms (such as + * Solaris 8) that claim to support ifindex retrieval but do + * not. + */ + c = if_lookup_address((void *)&iph->ip_src, AF_INET, + ospf->vrf_id); + if (c) + ifp = c->ifp; + if (ifp == NULL) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug( + "%s: Unable to determine incoming interface from: %pI4(%s)", + __func__, &iph->ip_src, + ospf_get_name(ospf)); + return OSPF_READ_CONTINUE; + } + } + + if (ospf->vrf_id == VRF_DEFAULT && ospf->vrf_id != ifp->vrf->vrf_id) { + /* + * We may have a situation where l3mdev_accept == 1 + * let's just kindly drop the packet and move on. + * ospf really really really does not like when + * we receive the same packet multiple times. + */ + return OSPF_READ_CONTINUE; + } + + /* Self-originated packet should be discarded silently. */ + if (ospf_if_lookup_by_local_addr(ospf, NULL, iph->ip_src)) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) { + zlog_debug( + "ospf_read[%pI4]: Dropping self-originated packet", + &iph->ip_src); + } + return OSPF_READ_CONTINUE; + } + + /* Check that we have enough for an IP header */ + if ((unsigned int)(iph->ip_hl << 2) >= STREAM_READABLE(ibuf)) { + if ((unsigned int)(iph->ip_hl << 2) == STREAM_READABLE(ibuf)) { + flog_warn( + EC_OSPF_PACKET, + "Rx'd IP packet with OSPF protocol number but no payload"); + } else { + flog_warn( + EC_OSPF_PACKET, + "IP header length field claims header is %u bytes, but we only have %zu", + (unsigned int)(iph->ip_hl << 2), + STREAM_READABLE(ibuf)); + } + + return OSPF_READ_ERROR; + } + stream_forward_getp(ibuf, iph->ip_hl << 2); + + ospfh = (struct ospf_header *)stream_pnt(ibuf); + if (MSG_OK + != ospf_packet_examin(ospfh, stream_get_endp(ibuf) + - stream_get_getp(ibuf))) + return OSPF_READ_CONTINUE; + /* Now it is safe to access all fields of OSPF packet header. */ + + /* associate packet with ospf interface */ + oi = ospf_if_lookup_recv_if(ospf, iph->ip_src, ifp); + + /* + * ospf_verify_header() relies on a valid "oi" and thus can be called + * only after the passive/backbone/other checks below are passed. + * These checks in turn access the fields of unverified "ospfh" + * structure for their own purposes and must remain very accurate + * in doing this. + */ + + /* If incoming interface is passive one, ignore it. */ + if (oi && OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_PASSIVE) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "ignoring packet from router %pI4 sent to %pI4, received on a passive interface, %pI4", + &ospfh->router_id, &iph->ip_dst, + &oi->address->u.prefix4); + + if (iph->ip_dst.s_addr == htonl(OSPF_ALLSPFROUTERS)) { + /* Try to fix multicast membership. + * Some OS:es may have problems in this area, + * make sure it is removed. + */ + OI_MEMBER_JOINED(oi, MEMBER_ALLROUTERS); + ospf_if_set_multicast(oi); + } + return OSPF_READ_CONTINUE; + } + + + /* if no local ospf_interface, + * or header area is backbone but ospf_interface is not + * check for VLINK interface + */ + if ((oi == NULL) + || (OSPF_IS_AREA_ID_BACKBONE(ospfh->area_id) + && !OSPF_IS_AREA_ID_BACKBONE(oi->area->area_id))) { + if ((oi = ospf_associate_packet_vl(ospf, ifp, iph, ospfh)) + == NULL) { + if (!ospf->instance && IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Packet from [%pI4] received on link %s but no ospf_interface", + &iph->ip_src, ifp->name); + return OSPF_READ_CONTINUE; + } + } + + /* + * else it must be a local ospf interface, check it was + * received on correct link + */ + else if (oi->ifp != ifp) { + if (IS_DEBUG_OSPF_EVENT) + flog_warn(EC_OSPF_PACKET, + "Packet from [%pI4] received on wrong link %s", + &iph->ip_src, ifp->name); + return OSPF_READ_CONTINUE; + } else if (oi->state == ISM_Down) { + flog_warn( + EC_OSPF_PACKET, + "Ignoring packet from %pI4 to %pI4 received on interface that is down [%s]; interface flags are %s", + &iph->ip_src, &iph->ip_dst, ifp->name, + if_flag_dump(ifp->flags)); + /* Fix multicast memberships? */ + if (iph->ip_dst.s_addr == htonl(OSPF_ALLSPFROUTERS)) + OI_MEMBER_JOINED(oi, MEMBER_ALLROUTERS); + else if (iph->ip_dst.s_addr == htonl(OSPF_ALLDROUTERS)) + OI_MEMBER_JOINED(oi, MEMBER_DROUTERS); + if (oi->multicast_memberships) + ospf_if_set_multicast(oi); + return OSPF_READ_CONTINUE; + } + + /* + * If the received packet is destined for AllDRouters, the + * packet should be accepted only if the received ospf + * interface state is either DR or Backup -- endo. + * + * I wonder who endo is? + */ + if (iph->ip_dst.s_addr == htonl(OSPF_ALLDROUTERS) + && (oi->state != ISM_DR && oi->state != ISM_Backup)) { + flog_warn( + EC_OSPF_PACKET, + "Dropping packet for AllDRouters from [%pI4] via [%s] (ISM: %s)", + &iph->ip_src, IF_NAME(oi), + lookup_msg(ospf_ism_state_msg, oi->state, NULL)); + /* Try to fix multicast membership. */ + SET_FLAG(oi->multicast_memberships, MEMBER_DROUTERS); + ospf_if_set_multicast(oi); + return OSPF_READ_CONTINUE; + } + + /* Verify more OSPF header fields. */ + ret = ospf_verify_header(ibuf, oi, iph, ospfh); + if (ret < 0) { + if (IS_DEBUG_OSPF_PACKET(0, RECV)) + zlog_debug( + "ospf_read[%pI4]: Header check failed, dropping.", + &iph->ip_src); + return OSPF_READ_CONTINUE; + } + + /* Show debug receiving packet. */ + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) { + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, DETAIL)) { + zlog_debug( + "-----------------------------------------------------"); + ospf_packet_dump(ibuf); + } + + zlog_debug("%s received from [%pI4] via [%s]", + lookup_msg(ospf_packet_type_str, ospfh->type, NULL), + &ospfh->router_id, IF_NAME(oi)); + zlog_debug(" src [%pI4],", &iph->ip_src); + zlog_debug(" dst [%pI4]", &iph->ip_dst); + + if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, DETAIL)) + zlog_debug( + "-----------------------------------------------------"); + } + + stream_forward_getp(ibuf, OSPF_HEADER_SIZE); + + /* Adjust size to message length. */ + length = ntohs(ospfh->length) - OSPF_HEADER_SIZE; + + /* Read rest of the packet and call each sort of packet routine. + */ + switch (ospfh->type) { + case OSPF_MSG_HELLO: + ospf_hello(iph, ospfh, ibuf, oi, length); + break; + case OSPF_MSG_DB_DESC: + ospf_db_desc(iph, ospfh, ibuf, oi, length); + break; + case OSPF_MSG_LS_REQ: + ospf_ls_req(iph, ospfh, ibuf, oi, length); + break; + case OSPF_MSG_LS_UPD: + ospf_ls_upd(ospf, iph, ospfh, ibuf, oi, length); + break; + case OSPF_MSG_LS_ACK: + ospf_ls_ack(iph, ospfh, ibuf, oi, length); + break; + default: + flog_warn( + EC_OSPF_PACKET, + "interface %s(%s): OSPF packet header type %d is illegal", + IF_NAME(oi), ospf_get_name(ospf), ospfh->type); + break; + } + + return OSPF_READ_CONTINUE; +} + +/* Starting point of packet process function. */ +void ospf_read(struct event *thread) +{ + struct ospf *ospf; + int32_t count = 0; + enum ospf_read_return_enum ret; + + /* first of all get interface pointer. */ + ospf = EVENT_ARG(thread); + + /* prepare for next packet. */ + event_add_read(master, ospf_read, ospf, ospf->fd, &ospf->t_read); + + while (count < ospf->write_oi_count) { + count++; + ret = ospf_read_helper(ospf); + switch (ret) { + case OSPF_READ_ERROR: + return; + case OSPF_READ_CONTINUE: + break; + } + } +} + +/* Make OSPF header. */ +static void ospf_make_header(int type, struct ospf_interface *oi, + struct stream *s) +{ + struct ospf_header *ospfh; + + ospfh = (struct ospf_header *)STREAM_DATA(s); + + ospfh->version = (uint8_t)OSPF_VERSION; + ospfh->type = (uint8_t)type; + + ospfh->router_id = oi->ospf->router_id; + + ospfh->checksum = 0; + ospfh->area_id = oi->area->area_id; + ospfh->auth_type = htons(ospf_auth_type(oi)); + + memset(ospfh->u.auth_data, 0, OSPF_AUTH_SIMPLE_SIZE); + + stream_forward_endp(s, OSPF_HEADER_SIZE); +} + +/* Fill rest of OSPF header. */ +static void ospf_fill_header(struct ospf_interface *oi, struct stream *s, + uint16_t length) +{ + struct ospf_header *ospfh; + + ospfh = (struct ospf_header *)STREAM_DATA(s); + + /* Fill length. */ + ospfh->length = htons(length); + + /* Calculate checksum. */ + if (ntohs(ospfh->auth_type) != OSPF_AUTH_CRYPTOGRAPHIC) + ospfh->checksum = in_cksum(ospfh, length); + else + ospfh->checksum = 0; + + /* Add Authentication Data. */ + oi->keychain = NULL; + oi->key = NULL; + ospf_auth_make_data(oi, ospfh); +} + +static int ospf_make_hello(struct ospf_interface *oi, struct stream *s) +{ + struct ospf_neighbor *nbr; + struct route_node *rn; + uint16_t length = OSPF_HELLO_MIN_SIZE; + struct in_addr mask; + unsigned long p; + int flag = 0; + + /* Set netmask of interface. */ + if (!(CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED) + && oi->type == OSPF_IFTYPE_POINTOPOINT) + && oi->type != OSPF_IFTYPE_VIRTUALLINK) + masklen2ip(oi->address->prefixlen, &mask); + else + memset((char *)&mask, 0, sizeof(struct in_addr)); + stream_put_ipv4(s, mask.s_addr); + + /* Set Hello Interval. */ + if (OSPF_IF_PARAM(oi, fast_hello) == 0) + stream_putw(s, OSPF_IF_PARAM(oi, v_hello)); + else + stream_putw(s, 0); /* hello-interval of 0 for fast-hellos */ + + /* Check if flood-reduction is enabled, + * if yes set the DC bit in the options. + */ + if (OSPF_FR_CONFIG(oi->ospf, oi->area)) + SET_FLAG(OPTIONS(oi), OSPF_OPTION_DC); + else if (CHECK_FLAG(OPTIONS(oi), OSPF_OPTION_DC)) + UNSET_FLAG(OPTIONS(oi), OSPF_OPTION_DC); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: options: %x, int: %s", __func__, OPTIONS(oi), + IF_NAME(oi)); + + /* Set Options. */ + stream_putc(s, OPTIONS(oi)); + + /* Set Router Priority. */ + stream_putc(s, PRIORITY(oi)); + + /* Set Router Dead Interval. */ + stream_putl(s, OSPF_IF_PARAM(oi, v_wait)); + + /* Set Designated Router. */ + stream_put_ipv4(s, DR(oi).s_addr); + + p = stream_get_endp(s); + + /* Set Backup Designated Router. */ + stream_put_ipv4(s, BDR(oi).s_addr); + + /* Add neighbor seen. */ + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + nbr = rn->info; + + if (!nbr) + continue; + + /* Ignore the 0.0.0.0 node */ + if (nbr->router_id.s_addr == INADDR_ANY) + continue; + + /* Ignore Down neighbor */ + if (nbr->state == NSM_Attempt) + continue; + + /* This is myself for DR election */ + if (nbr->state == NSM_Down) + continue; + + if (IPV4_ADDR_SAME(&nbr->router_id, &oi->ospf->router_id)) + continue; + /* Check neighbor is sane? */ + if (nbr->d_router.s_addr != INADDR_ANY && + IPV4_ADDR_SAME(&nbr->d_router, &oi->address->u.prefix4) && + IPV4_ADDR_SAME(&nbr->bd_router, &oi->address->u.prefix4)) + flag = 1; + + /* Hello packet overflows interface MTU. + */ + if (length + sizeof(uint32_t) > ospf_packet_max(oi)) { + flog_err( + EC_OSPF_LARGE_HELLO, + "Oversized Hello packet! Larger than MTU. Not sending it out"); + return 0; + } + + stream_put_ipv4(s, nbr->router_id.s_addr); + length += 4; + } + + /* Let neighbor generate BackupSeen. */ + if (flag == 1) + stream_putl_at(s, p, 0); /* ipv4 address, normally */ + + return length; +} + +static int ospf_make_db_desc(struct ospf_interface *oi, + struct ospf_neighbor *nbr, struct stream *s) +{ + struct ospf_lsa *lsa; + uint16_t length = OSPF_DB_DESC_MIN_SIZE; + uint8_t options; + unsigned long pp; + int i; + struct ospf_lsdb *lsdb; + + /* Set Interface MTU. */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + stream_putw(s, 0); + else + stream_putw(s, oi->ifp->mtu); + + /* Set Options. */ + options = OPTIONS(oi); + if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) && + OSPF_IF_PARAM(oi, opaque_capable)) + SET_FLAG(options, OSPF_OPTION_O); + if (OSPF_FR_CONFIG(oi->ospf, oi->area)) + SET_FLAG(options, OSPF_OPTION_DC); + stream_putc(s, options); + + /* DD flags */ + pp = stream_get_endp(s); + stream_putc(s, nbr->dd_flags); + + /* Set DD Sequence Number. */ + stream_putl(s, nbr->dd_seqnum); + + /* shortcut unneeded walk of (empty) summary LSDBs */ + if (ospf_db_summary_isempty(nbr)) + goto empty; + + /* Describe LSA Header from Database Summary List. */ + lsdb = &nbr->db_sum; + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) { + struct route_table *table = lsdb->type[i].db; + struct route_node *rn; + + for (rn = route_top(table); rn; rn = route_next(rn)) + if ((lsa = rn->info) != NULL) { + if (IS_OPAQUE_LSA(lsa->data->type) + && (!CHECK_FLAG(options, OSPF_OPTION_O))) { + /* Suppress advertising + * opaque-information. */ + /* Remove LSA from DB summary list. */ + ospf_lsdb_delete(lsdb, lsa); + continue; + } + + if (!CHECK_FLAG(lsa->flags, OSPF_LSA_DISCARD)) { + struct lsa_header *lsah; + uint16_t ls_age; + + /* DD packet overflows interface MTU. */ + if (length + OSPF_LSA_HEADER_SIZE + > ospf_packet_max(oi)) + break; + + /* Keep pointer to LS age. */ + lsah = (struct lsa_header + *)(STREAM_DATA(s) + + stream_get_endp( + s)); + + /* Proceed stream pointer. */ + stream_put(s, lsa->data, + OSPF_LSA_HEADER_SIZE); + length += OSPF_LSA_HEADER_SIZE; + + /* Set LS age. */ + ls_age = LS_AGE(lsa); + lsah->ls_age = htons(ls_age); + } + + /* Remove LSA from DB summary list. */ + ospf_lsdb_delete(lsdb, lsa); + } + } + + /* Update 'More' bit */ + if (ospf_db_summary_isempty(nbr)) { + empty: + if (nbr->state >= NSM_Exchange) { + UNSET_FLAG(nbr->dd_flags, OSPF_DD_FLAG_M); + /* Rewrite DD flags */ + stream_putc_at(s, pp, nbr->dd_flags); + } else { + assert(IS_SET_DD_M(nbr->dd_flags)); + } + } + return length; +} + +static int ospf_make_ls_req_func(struct stream *s, uint16_t *length, + unsigned long delta, struct ospf_neighbor *nbr, + struct ospf_lsa *lsa) +{ + struct ospf_interface *oi; + + oi = nbr->oi; + + /* LS Request packet overflows interface MTU + * delta is just number of bytes required for 1 LS Req + * ospf_packet_max will return the number of bytes can + * be accommodated without ospf header. So length+delta + * can be compared to ospf_packet_max + * to check if it can fit another lsreq in the same packet. + */ + + if (*length + delta > ospf_packet_max(oi)) + return 0; + + stream_putl(s, lsa->data->type); + stream_put_ipv4(s, lsa->data->id.s_addr); + stream_put_ipv4(s, lsa->data->adv_router.s_addr); + + ospf_lsa_unlock(&nbr->ls_req_last); + nbr->ls_req_last = ospf_lsa_lock(lsa); + + *length += 12; + return 1; +} + +static int ospf_make_ls_req(struct ospf_neighbor *nbr, struct stream *s) +{ + struct ospf_lsa *lsa; + uint16_t length = OSPF_LS_REQ_MIN_SIZE; + unsigned long delta = 12; + struct route_table *table; + struct route_node *rn; + int i; + struct ospf_lsdb *lsdb; + + lsdb = &nbr->ls_req; + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) { + table = lsdb->type[i].db; + for (rn = route_top(table); rn; rn = route_next(rn)) + if ((lsa = (rn->info)) != NULL) + if (ospf_make_ls_req_func(s, &length, delta, + nbr, lsa) + == 0) { + route_unlock_node(rn); + break; + } + } + return length; +} + +static int ls_age_increment(struct ospf_lsa *lsa, int delay) +{ + int age; + + age = IS_LSA_MAXAGE(lsa) ? OSPF_LSA_MAXAGE : LS_AGE(lsa) + delay; + + return (age > OSPF_LSA_MAXAGE ? OSPF_LSA_MAXAGE : age); +} + +static int ospf_make_ls_upd(struct ospf_interface *oi, struct list *update, + struct stream *s) +{ + struct ospf_lsa *lsa; + struct listnode *node; + uint16_t length = 0; + unsigned int size_noauth; + unsigned long delta = stream_get_endp(s); + unsigned long pp; + int count = 0; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Start", __func__); + + pp = stream_get_endp(s); + stream_forward_endp(s, OSPF_LS_UPD_MIN_SIZE); + length += OSPF_LS_UPD_MIN_SIZE; + + /* Calculate amount of packet usable for data. */ + size_noauth = stream_get_size(s) - ospf_packet_authspace(oi); + + while ((node = listhead(update)) != NULL) { + struct lsa_header *lsah; + uint16_t ls_age; + + lsa = listgetdata(node); + assert(lsa->data); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: List Iteration %d LSA[%s]", __func__, + count, dump_lsa_key(lsa)); + + /* Will it fit? Minimum it has to fit at least one */ + if ((length + delta + ntohs(lsa->data->length) > size_noauth) && + (count > 0)) + break; + + /* Keep pointer to LS age. */ + lsah = (struct lsa_header *)(STREAM_DATA(s) + + stream_get_endp(s)); + + /* Put LSA to Link State Request. */ + stream_put(s, lsa->data, ntohs(lsa->data->length)); + + /* Set LS age. */ + /* each hop must increment an lsa_age by transmit_delay + of OSPF interface */ + ls_age = ls_age_increment(lsa, + OSPF_IF_PARAM(oi, transmit_delay)); + lsah->ls_age = htons(ls_age); + + length += ntohs(lsa->data->length); + count++; + + list_delete_node(update, node); + ospf_lsa_unlock(&lsa); /* oi->ls_upd_queue */ + } + + /* Now set #LSAs. */ + stream_putl_at(s, pp, count); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Stop", __func__); + return length; +} + +static int ospf_make_ls_ack(struct ospf_interface *oi, struct list *ack, + struct stream *s) +{ + struct listnode *node, *nnode; + uint16_t length = OSPF_LS_ACK_MIN_SIZE; + unsigned long delta = OSPF_LSA_HEADER_SIZE; + struct ospf_lsa *lsa; + + for (ALL_LIST_ELEMENTS(ack, node, nnode, lsa)) { + assert(lsa); + + /* LS Ack packet overflows interface MTU + * delta is just number of bytes required for + * 1 LS Ack(1 LS Hdr) ospf_packet_max will return + * the number of bytes can be accommodated without + * ospf header. So length+delta can be compared + * against ospf_packet_max to check if it can fit + * another ls header in the same packet. + */ + if ((length + delta) > ospf_packet_max(oi)) + break; + + stream_put(s, lsa->data, OSPF_LSA_HEADER_SIZE); + length += OSPF_LSA_HEADER_SIZE; + + listnode_delete(ack, lsa); + ospf_lsa_unlock(&lsa); /* oi->ls_ack_direct.ls_ack */ + } + + return length; +} + +static void ospf_hello_send_sub(struct ospf_interface *oi, in_addr_t addr) +{ + struct ospf_packet *op; + uint16_t length = OSPF_HEADER_SIZE; + + /* Check if config is still being processed */ + if (event_is_scheduled(t_ospf_cfg)) { + if (IS_DEBUG_OSPF_PACKET(0, SEND)) + zlog_debug( + "Suppressing hello to %pI4 on %s during config load", + &(addr), IF_NAME(oi)); + + return; + } + + op = ospf_packet_new(oi->ifp->mtu); + + /* Prepare OSPF common header. */ + ospf_make_header(OSPF_MSG_HELLO, oi, op->s); + + /* Prepare OSPF Hello body. */ + length += ospf_make_hello(oi, op->s); + if (length == OSPF_HEADER_SIZE) { + /* Hello overshooting MTU */ + ospf_packet_free(op); + return; + } + + /* Fill OSPF header. */ + ospf_fill_header(oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + op->dst.s_addr = addr; + + if (IS_DEBUG_OSPF_EVENT) { + if (oi->ospf->vrf_id) + zlog_debug( + "%s: Hello Tx interface %s ospf vrf %s id %u", + __func__, oi->ifp->name, + ospf_vrf_id_to_name(oi->ospf->vrf_id), + oi->ospf->vrf_id); + } + /* Add packet to the top of the interface output queue, so that they + * can't get delayed by things like long queues of LS Update packets + */ + ospf_packet_add_top(oi, op); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON(oi->ospf); +} + +static void ospf_poll_send(struct ospf_nbr_nbma *nbr_nbma) +{ + struct ospf_interface *oi; + + oi = nbr_nbma->oi; + assert(oi); + + /* If this is passive interface, do not send OSPF Hello. */ + if (OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_PASSIVE) + return; + + if (oi->type != OSPF_IFTYPE_NBMA) + return; + + if (nbr_nbma->nbr != NULL && nbr_nbma->nbr->state != NSM_Down) + return; + + if (PRIORITY(oi) == 0) + return; + + if (nbr_nbma->priority == 0 && oi->state != ISM_DR + && oi->state != ISM_Backup) + return; + + ospf_hello_send_sub(oi, nbr_nbma->addr.s_addr); +} + +void ospf_poll_timer(struct event *thread) +{ + struct ospf_nbr_nbma *nbr_nbma; + + nbr_nbma = EVENT_ARG(thread); + nbr_nbma->t_poll = NULL; + + if (IS_DEBUG_OSPF(nsm, NSM_TIMERS)) + zlog_debug("NSM[%s:%pI4]: Timer (Poll timer expire)", + IF_NAME(nbr_nbma->oi), &nbr_nbma->addr); + + ospf_poll_send(nbr_nbma); + + if (nbr_nbma->v_poll > 0) + OSPF_POLL_TIMER_ON(nbr_nbma->t_poll, ospf_poll_timer, + nbr_nbma->v_poll); +} + + +void ospf_hello_reply_timer(struct event *thread) +{ + struct ospf_neighbor *nbr; + + nbr = EVENT_ARG(thread); + nbr->t_hello_reply = NULL; + + if (IS_DEBUG_OSPF(nsm, NSM_TIMERS)) + zlog_debug("NSM[%s:%pI4]: Timer (hello-reply timer expire)", + IF_NAME(nbr->oi), &nbr->router_id); + + ospf_hello_send_sub(nbr->oi, nbr->address.u.prefix4.s_addr); +} + +/* Send OSPF Hello. */ +void ospf_hello_send(struct ospf_interface *oi) +{ + /* If this is passive interface, do not send OSPF Hello. */ + if (OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_PASSIVE) + return; + + if (oi->type == OSPF_IFTYPE_NBMA) { + struct ospf_neighbor *nbr; + struct route_node *rn; + + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + nbr = rn->info; + if (!nbr) + continue; + + if (nbr == oi->nbr_self) + continue; + + if (nbr->state == NSM_Down) + continue; + + /* + * RFC 2328 Section 9.5.1 + * If the router is not eligible to become Designated + * Router, it must periodically send Hello Packets to + * both the Designated Router and the Backup + * Designated Router (if they exist). + */ + if (PRIORITY(oi) == 0 && + IPV4_ADDR_CMP(&DR(oi), &nbr->address.u.prefix4) && + IPV4_ADDR_CMP(&BDR(oi), &nbr->address.u.prefix4)) + continue; + + /* + * If the router is eligible to become Designated + * Router, it must periodically send Hello Packets to + * all neighbors that are also eligible. In addition, + * if the router is itself the Designated Router or + * Backup Designated Router, it must also send periodic + * Hello Packets to all other neighbors. + */ + if (nbr->priority == 0 && oi->state == ISM_DROther) + continue; + + /* if oi->state == Waiting, send + * hello to all neighbors */ + ospf_hello_send_sub(oi, nbr->address.u.prefix4.s_addr); + } + } else { + /* Decide destination address. */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + ospf_hello_send_sub(oi, oi->vl_data->peer_addr.s_addr); + else + ospf_hello_send_sub(oi, htonl(OSPF_ALLSPFROUTERS)); + } +} + +/* Send OSPF Database Description. */ +void ospf_db_desc_send(struct ospf_neighbor *nbr) +{ + struct ospf_interface *oi; + struct ospf_packet *op; + uint16_t length = OSPF_HEADER_SIZE; + + oi = nbr->oi; + op = ospf_packet_new(oi->ifp->mtu); + + /* Prepare OSPF common header. */ + ospf_make_header(OSPF_MSG_DB_DESC, oi, op->s); + + /* Prepare OSPF Database Description body. */ + length += ospf_make_db_desc(oi, nbr, op->s); + + /* Fill OSPF header. */ + ospf_fill_header(oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + /* Decide destination address. */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT) + op->dst.s_addr = htonl(OSPF_ALLSPFROUTERS); + else + op->dst = nbr->address.u.prefix4; + + /* Add packet to the interface output queue. */ + ospf_packet_add(oi, op); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON(oi->ospf); + + /* Remove old DD packet, then copy new one and keep in neighbor + * structure. */ + if (nbr->last_send) + ospf_packet_free(nbr->last_send); + nbr->last_send = ospf_packet_dup(op); + monotime(&nbr->last_send_ts); + if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) + zlog_info( + "%s:Packet[DD]: %pI4 DB Desc send with seqnum:%x , flags:%x", + ospf_get_name(oi->ospf), &nbr->router_id, + nbr->dd_seqnum, nbr->dd_flags); +} + +/* Re-send Database Description. */ +void ospf_db_desc_resend(struct ospf_neighbor *nbr) +{ + struct ospf_interface *oi; + + oi = nbr->oi; + + /* Add packet to the interface output queue. */ + ospf_packet_add(oi, ospf_packet_dup(nbr->last_send)); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON(oi->ospf); + if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) + zlog_info( + "%s:Packet[DD]: %pI4 DB Desc resend with seqnum:%x , flags:%x", + ospf_get_name(oi->ospf), &nbr->router_id, + nbr->dd_seqnum, nbr->dd_flags); +} + +/* Send Link State Request. */ +void ospf_ls_req_send(struct ospf_neighbor *nbr) +{ + struct ospf_interface *oi; + struct ospf_packet *op; + uint16_t length = OSPF_HEADER_SIZE; + + oi = nbr->oi; + op = ospf_packet_new(oi->ifp->mtu); + + /* Prepare OSPF common header. */ + ospf_make_header(OSPF_MSG_LS_REQ, oi, op->s); + + /* Prepare OSPF Link State Request body. */ + length += ospf_make_ls_req(nbr, op->s); + if (length == OSPF_HEADER_SIZE) { + ospf_packet_free(op); + return; + } + + /* Fill OSPF header. */ + ospf_fill_header(oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + /* Decide destination address. */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT) + op->dst.s_addr = htonl(OSPF_ALLSPFROUTERS); + else + op->dst = nbr->address.u.prefix4; + + /* Add packet to the interface output queue. */ + ospf_packet_add(oi, op); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON(oi->ospf); + + /* Add Link State Request Retransmission Timer. */ + OSPF_NSM_TIMER_ON(nbr->t_ls_req, ospf_ls_req_timer, nbr->v_ls_req); +} + +/* Send Link State Update with an LSA. */ +void ospf_ls_upd_send_lsa(struct ospf_neighbor *nbr, struct ospf_lsa *lsa, + int flag) +{ + struct list *update; + + update = list_new(); + + listnode_add(update, lsa); + + /*ospf instance is going down, send self originated + * MAXAGE LSA update to neighbors to remove from LSDB */ + if (nbr->oi->ospf->inst_shutdown && IS_LSA_MAXAGE(lsa)) + ospf_ls_upd_send(nbr, update, flag, 1); + else + ospf_ls_upd_send(nbr, update, flag, 0); + + list_delete(&update); +} + +/* Determine size for packet. Must be at least big enough to accommodate next + * LSA on list, which may be bigger than MTU size. + * + * Return pointer to new ospf_packet + * NULL if we can not allocate, eg because LSA is bigger than imposed limit + * on packet sizes (in which case offending LSA is deleted from update list) + */ +static struct ospf_packet *ospf_ls_upd_packet_new(struct list *update, + struct ospf_interface *oi) +{ + struct ospf_lsa *lsa; + struct listnode *ln; + size_t size; + static char warned = 0; + + lsa = listgetdata((ln = listhead(update))); + assert(lsa->data); + + if ((OSPF_LS_UPD_MIN_SIZE + ntohs(lsa->data->length)) + > ospf_packet_max(oi)) { + if (!warned) { + flog_warn( + EC_OSPF_LARGE_LSA, + "%s: oversized LSA encountered!will need to fragment. Not optimal. Try divide up your network with areas. Use 'debug ospf packet send' to see details, or look at 'show ip ospf database ..'", + __func__); + warned = 1; + } + + if (IS_DEBUG_OSPF_PACKET(0, SEND)) + zlog_debug( + "%s: oversized LSA id:%pI4, %d bytes originated by %pI4, will be fragmented!", + __func__, &lsa->data->id, + ntohs(lsa->data->length), + &lsa->data->adv_router); + + /* + * Allocate just enough to fit this LSA only, to avoid including + * other + * LSAs in fragmented LSA Updates. + */ + size = ntohs(lsa->data->length) + + (oi->ifp->mtu - ospf_packet_max(oi)) + + OSPF_LS_UPD_MIN_SIZE; + } else + size = oi->ifp->mtu; + + if (size > OSPF_MAX_PACKET_SIZE) { + flog_warn( + EC_OSPF_LARGE_LSA, + "%s: oversized LSA id:%pI4 too big, %d bytes, packet size %ld, dropping it completely. OSPF routing is broken!", + __func__, &lsa->data->id, ntohs(lsa->data->length), + (long int)size); + list_delete_node(update, ln); + return NULL; + } + + /* IP header is built up separately by ospf_write(). This means, that we + * must + * reduce the "affordable" size just calculated by length of an IP + * header. + * This makes sure, that even if we manage to fill the payload with LSA + * data + * completely, the final packet (our data plus IP header) still fits + * into + * outgoing interface MTU. This correction isn't really meaningful for + * an + * oversized LSA, but for consistency the correction is done for both + * cases. + * + * P.S. OSPF_MAX_PACKET_SIZE above already includes IP header size + */ + return ospf_packet_new(size - sizeof(struct ip)); +} + +void ospf_ls_upd_queue_send(struct ospf_interface *oi, struct list *update, + struct in_addr addr, int send_lsupd_now) +{ + struct ospf_packet *op; + uint16_t length = OSPF_HEADER_SIZE; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("listcount = %d, [%s]dst %pI4", listcount(update), + IF_NAME(oi), &addr); + + /* Check that we have really something to process */ + if (listcount(update) == 0) + return; + + op = ospf_ls_upd_packet_new(update, oi); + + /* Prepare OSPF common header. */ + ospf_make_header(OSPF_MSG_LS_UPD, oi, op->s); + + /* Prepare OSPF Link State Update body. + * Includes Type-7 translation. + */ + length += ospf_make_ls_upd(oi, update, op->s); + + /* Fill OSPF header. */ + ospf_fill_header(oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + /* Decide destination address. */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT) + op->dst.s_addr = htonl(OSPF_ALLSPFROUTERS); + else + op->dst.s_addr = addr.s_addr; + + /* Add packet to the interface output queue. */ + ospf_packet_add(oi, op); + /* Call ospf_write() right away to send ospf packets to neighbors */ + if (send_lsupd_now) { + struct event os_packet_thd; + + os_packet_thd.arg = (void *)oi->ospf; + if (oi->on_write_q == 0) { + listnode_add(oi->ospf->oi_write_q, oi); + oi->on_write_q = 1; + } + ospf_write(&os_packet_thd); + /* + * We are fake calling ospf_write with a fake + * thread. Imagine that we have oi_a already + * enqueued and we have turned on the write + * thread(t_write). + * Now this function calls this for oi_b + * so the on_write_q has oi_a and oi_b on + * it, ospf_write runs and clears the packets + * for both oi_a and oi_b. Removing them from + * the on_write_q. After this thread of execution + * finishes we will execute the t_write thread + * with nothing in the on_write_q causing an + * assert. So just make sure that the t_write + * is actually turned off. + */ + if (list_isempty(oi->ospf->oi_write_q)) + EVENT_OFF(oi->ospf->t_write); + } else { + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON(oi->ospf); + } +} + +static void ospf_ls_upd_send_queue_event(struct event *thread) +{ + struct ospf_interface *oi = EVENT_ARG(thread); + struct route_node *rn; + struct route_node *rnext; + struct list *update; + char again = 0; + + oi->t_ls_upd_event = NULL; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s start", __func__); + + for (rn = route_top(oi->ls_upd_queue); rn; rn = rnext) { + rnext = route_next(rn); + + if (rn->info == NULL) + continue; + + update = (struct list *)rn->info; + + ospf_ls_upd_queue_send(oi, update, rn->p.u.prefix4, 0); + + /* list might not be empty. */ + if (listcount(update) == 0) { + list_delete((struct list **)&rn->info); + route_unlock_node(rn); + } else + again = 1; + } + + if (again != 0) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: update lists not cleared, %d nodes to try again, raising new event", + __func__, again); + oi->t_ls_upd_event = NULL; + event_add_event(master, ospf_ls_upd_send_queue_event, oi, 0, + &oi->t_ls_upd_event); + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s stop", __func__); +} + +void ospf_ls_upd_send(struct ospf_neighbor *nbr, struct list *update, int flag, + int send_lsupd_now) +{ + struct ospf_interface *oi; + struct ospf_lsa *lsa; + struct prefix_ipv4 p; + struct route_node *rn; + struct listnode *node; + + oi = nbr->oi; + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + + /* Decide destination address. */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + p.prefix = oi->vl_data->peer_addr; + else if (oi->type == OSPF_IFTYPE_POINTOPOINT) + p.prefix.s_addr = htonl(OSPF_ALLSPFROUTERS); + else if (flag == OSPF_SEND_PACKET_DIRECT) + p.prefix = nbr->address.u.prefix4; + else if (oi->state == ISM_DR || oi->state == ISM_Backup) + p.prefix.s_addr = htonl(OSPF_ALLSPFROUTERS); + else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) + p.prefix.s_addr = htonl(OSPF_ALLSPFROUTERS); + else + p.prefix.s_addr = htonl(OSPF_ALLDROUTERS); + + if (oi->type == OSPF_IFTYPE_NBMA) { + if (flag == OSPF_SEND_PACKET_INDIRECT) + flog_warn( + EC_OSPF_PACKET, + "* LS-Update is directly sent on NBMA network."); + if (IPV4_ADDR_SAME(&oi->address->u.prefix4, &p.prefix)) + flog_warn(EC_OSPF_PACKET, + "* LS-Update is sent to myself."); + } + + rn = route_node_get(oi->ls_upd_queue, (struct prefix *)&p); + + if (rn->info == NULL) + rn->info = list_new(); + else + route_unlock_node(rn); + + for (ALL_LIST_ELEMENTS_RO(update, node, lsa)) + listnode_add(rn->info, + ospf_lsa_lock(lsa)); /* oi->ls_upd_queue */ + if (send_lsupd_now) { + struct list *send_update_list; + struct route_node *rnext; + + for (rn = route_top(oi->ls_upd_queue); rn; rn = rnext) { + rnext = route_next(rn); + + if (rn->info == NULL) + continue; + + send_update_list = (struct list *)rn->info; + + ospf_ls_upd_queue_send(oi, send_update_list, + rn->p.u.prefix4, 1); + } + } else + event_add_event(master, ospf_ls_upd_send_queue_event, oi, 0, + &oi->t_ls_upd_event); +} + +static void ospf_ls_ack_send_list(struct ospf_interface *oi, struct list *ack, + struct in_addr dst) +{ + struct ospf_packet *op; + uint16_t length = OSPF_HEADER_SIZE; + + op = ospf_packet_new(oi->ifp->mtu); + + /* Prepare OSPF common header. */ + ospf_make_header(OSPF_MSG_LS_ACK, oi, op->s); + + /* Prepare OSPF Link State Acknowledgment body. */ + length += ospf_make_ls_ack(oi, ack, op->s); + + /* Fill OSPF header. */ + ospf_fill_header(oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + /* Decide destination address. */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT || + oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) + op->dst.s_addr = htonl(OSPF_ALLSPFROUTERS); + else + op->dst.s_addr = dst.s_addr; + + /* Add packet to the interface output queue. */ + ospf_packet_add(oi, op); + + /* Hook thread to write packet. */ + OSPF_ISM_WRITE_ON(oi->ospf); +} + +static void ospf_ls_ack_send_event(struct event *thread) +{ + struct ospf_interface *oi = EVENT_ARG(thread); + + oi->t_ls_ack_direct = NULL; + + while (listcount(oi->ls_ack_direct.ls_ack)) + ospf_ls_ack_send_list(oi, oi->ls_ack_direct.ls_ack, + oi->ls_ack_direct.dst); +} + +void ospf_ls_ack_send(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) +{ + struct ospf_interface *oi = nbr->oi; + + if (IS_GRACE_LSA(lsa)) { + if (IS_DEBUG_OSPF_GR) + zlog_debug("%s, Sending GRACE ACK to Restarter.", + __func__); + } + + if (listcount(oi->ls_ack_direct.ls_ack) == 0) + oi->ls_ack_direct.dst = nbr->address.u.prefix4; + + listnode_add(oi->ls_ack_direct.ls_ack, ospf_lsa_lock(lsa)); + + event_add_event(master, ospf_ls_ack_send_event, oi, 0, + &oi->t_ls_ack_direct); +} + +/* Send Link State Acknowledgment delayed. */ +void ospf_ls_ack_send_delayed(struct ospf_interface *oi) +{ + struct in_addr dst; + + /* Decide destination address. */ + /* RFC2328 Section 13.5 On non-broadcast + networks, delayed Link State Acknowledgment packets must be + unicast separately over each adjacency (i.e., neighbor whose + state is >= Exchange). */ + if (oi->type == OSPF_IFTYPE_NBMA) { + struct ospf_neighbor *nbr; + struct route_node *rn; + + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + nbr = rn->info; + + if (!nbr) + continue; + + if (nbr != oi->nbr_self && nbr->state >= NSM_Exchange) + while (listcount(oi->ls_ack)) + ospf_ls_ack_send_list( + oi, oi->ls_ack, + nbr->address.u.prefix4); + } + return; + } + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + dst.s_addr = oi->vl_data->peer_addr.s_addr; + else if (oi->state == ISM_DR || oi->state == ISM_Backup) + dst.s_addr = htonl(OSPF_ALLSPFROUTERS); + else if (oi->type == OSPF_IFTYPE_POINTOPOINT) + dst.s_addr = htonl(OSPF_ALLSPFROUTERS); + else if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) + dst.s_addr = htonl(OSPF_ALLSPFROUTERS); + else + dst.s_addr = htonl(OSPF_ALLDROUTERS); + + while (listcount(oi->ls_ack)) + ospf_ls_ack_send_list(oi, oi->ls_ack, dst); +} + +/* + * On pt-to-pt links, all OSPF control packets are sent to the multicast + * address. As a result, the kernel does not need to learn the interface + * MAC of the OSPF neighbor. However, in our world, this will delay + * convergence. Take the case when due to a link flap, all routes now + * want to use an interface which was deemed to be costlier prior to this + * event. For routes that will be installed, the missing MAC will have + * punt-to-CPU set on them. This may overload the CPU control path that + * can be avoided if the MAC was known apriori. + */ +void ospf_proactively_arp(struct ospf_neighbor *nbr) +{ + if (!nbr || !nbr->oi->ospf->proactive_arp) + return; + + ospf_zebra_send_arp(nbr->oi->ifp, &nbr->address); +} diff --git a/ospfd/ospf_packet.h b/ospfd/ospf_packet.h new file mode 100644 index 0000000..2347389 --- /dev/null +++ b/ospfd/ospf_packet.h @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF Sending and Receiving OSPF Packets. + * Copyright (C) 1999 Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPF_PACKET_H +#define _ZEBRA_OSPF_PACKET_H + +#define OSPF_HEADER_SIZE 24U +#define OSPF_AUTH_SIMPLE_SIZE 8U +#define OSPF_AUTH_MD5_SIZE 16U + +#define OSPF_MAX_PACKET_SIZE 65535U /* includes IP Header size. */ +#define OSPF_HELLO_MIN_SIZE 20U /* not including neighbors */ +#define OSPF_DB_DESC_MIN_SIZE 8U +#define OSPF_LS_REQ_MIN_SIZE 0U +#define OSPF_LS_UPD_MIN_SIZE 4U +#define OSPF_LS_ACK_MIN_SIZE 0U + +#define OSPF_MSG_HELLO 1 /* OSPF Hello Message. */ +#define OSPF_MSG_DB_DESC 2 /* OSPF Database Description Message. */ +#define OSPF_MSG_LS_REQ 3 /* OSPF Link State Request Message. */ +#define OSPF_MSG_LS_UPD 4 /* OSPF Link State Update Message. */ +#define OSPF_MSG_LS_ACK 5 /* OSPF Link State Acknowledgement Message. */ + +#define OSPF_SEND_PACKET_DIRECT 1 +#define OSPF_SEND_PACKET_INDIRECT 2 +#define OSPF_SEND_PACKET_LOOP 3 + +#define OSPF_HELLO_REPLY_DELAY 1 + +/* Return values of functions involved in packet verification, see ospf6d. */ +#define MSG_OK 0 +#define MSG_NG 1 + +struct ospf_packet { + struct ospf_packet *next; + + /* Pointer to data stream. */ + struct stream *s; + + /* IP destination address. */ + struct in_addr dst; + + /* OSPF packet length. */ + uint16_t length; +}; + +/* OSPF packet queue structure. */ +struct ospf_fifo { + unsigned long count; + + struct ospf_packet *head; + struct ospf_packet *tail; +}; + +/* OSPF packet header structure. */ +struct ospf_header { + uint8_t version; /* OSPF Version. */ + uint8_t type; /* Packet Type. */ + uint16_t length; /* Packet Length. */ + struct in_addr router_id; /* Router ID. */ + struct in_addr area_id; /* Area ID. */ + uint16_t checksum; /* Check Sum. */ + uint16_t auth_type; /* Authentication Type. */ + /* Authentication Data. */ + union { + /* Simple Authentication. */ + uint8_t auth_data[OSPF_AUTH_SIMPLE_SIZE]; + /* Cryptographic Authentication. */ + struct { + uint16_t zero; /* Should be 0. */ + uint8_t key_id; /* Key ID. */ + uint8_t auth_data_len; /* Auth Data Length. */ + uint32_t crypt_seqnum; /* Cryptographic Sequence + Number. */ + } crypt; + } u; +}; + +/* OSPF Hello body format. */ +struct ospf_hello { + struct in_addr network_mask; + uint16_t hello_interval; + uint8_t options; + uint8_t priority; + uint32_t dead_interval; + struct in_addr d_router; + struct in_addr bd_router; + struct in_addr neighbors[1]; +}; + +/* OSPF Database Description body format. */ +struct ospf_db_desc { + uint16_t mtu; + uint8_t options; + uint8_t flags; + uint32_t dd_seqnum; +}; + +struct ospf_ls_update { + uint32_t num_lsas; +}; + +/* Macros. */ +/* XXX Perhaps obsolete; function in ospf_packet.c */ +#define OSPF_PACKET_MAX(oi) ospf_packet_max (oi) + +#define OSPF_OUTPUT_PNT(S) ((S)->data + (S)->putp) +#define OSPF_OUTPUT_LENGTH(S) ((S)->endp) + +#define IS_SET_DD_MS(X) ((X) & OSPF_DD_FLAG_MS) +#define IS_SET_DD_M(X) ((X) & OSPF_DD_FLAG_M) +#define IS_SET_DD_I(X) ((X) & OSPF_DD_FLAG_I) +#define IS_SET_DD_ALL(X) ((X) & OSPF_DD_FLAG_ALL) + +/* Prototypes. */ +extern void ospf_packet_free(struct ospf_packet *); +extern struct ospf_fifo *ospf_fifo_new(void); +extern void ospf_fifo_push(struct ospf_fifo *, struct ospf_packet *); +extern struct ospf_packet *ospf_fifo_pop(struct ospf_fifo *); +extern struct ospf_packet *ospf_fifo_head(struct ospf_fifo *); +extern void ospf_fifo_flush(struct ospf_fifo *); +extern void ospf_fifo_free(struct ospf_fifo *); + +extern void ospf_read(struct event *thread); +extern void ospf_hello_send(struct ospf_interface *); +extern void ospf_db_desc_send(struct ospf_neighbor *); +extern void ospf_db_desc_resend(struct ospf_neighbor *); +extern void ospf_ls_req_send(struct ospf_neighbor *); +extern void ospf_ls_upd_send_lsa(struct ospf_neighbor *, struct ospf_lsa *, + int); +extern void ospf_ls_upd_send(struct ospf_neighbor *, struct list *, int, int); +extern void ospf_ls_upd_queue_send(struct ospf_interface *oi, + struct list *update, struct in_addr addr, + int send_lsupd_now); +extern void ospf_ls_ack_send(struct ospf_neighbor *, struct ospf_lsa *); +extern void ospf_ls_ack_send_delayed(struct ospf_interface *); +extern void ospf_ls_retransmit(struct ospf_interface *, struct ospf_lsa *); +extern void ospf_ls_req_event(struct ospf_neighbor *); + +extern void ospf_ls_upd_timer(struct event *thread); +extern void ospf_ls_ack_timer(struct event *thread); +extern void ospf_poll_timer(struct event *thread); +extern void ospf_hello_reply_timer(struct event *thread); + +extern const struct message ospf_packet_type_str[]; +extern const size_t ospf_packet_type_str_max; + +extern void ospf_proactively_arp(struct ospf_neighbor *); + +#endif /* _ZEBRA_OSPF_PACKET_H */ diff --git a/ospfd/ospf_ri.c b/ospfd/ospf_ri.c new file mode 100644 index 0000000..725443f --- /dev/null +++ b/ospfd/ospf_ri.c @@ -0,0 +1,2123 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This is an implementation of RFC4970 Router Information + * with support of RFC5088 PCE Capabilites announcement + * + * Module name: Router Information + * Author: Olivier Dugeon + * Copyright (C) 2012 - 2017 Orange Labs http://www.orange.com/ + */ + +#include +#include + +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "memory.h" +#include "command.h" +#include "vty.h" +#include "stream.h" +#include "log.h" +#include "frrevent.h" +#include "hash.h" +#include "sockunion.h" /* for inet_aton() */ +#include "mpls.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_sr.h" +#include "ospfd/ospf_ri.h" +#include "ospfd/ospf_errors.h" + +/* + * Global variable to manage Opaque-LSA/Router Information on this node. + * Note that all parameter values are stored in network byte order. + */ +static struct ospf_router_info OspfRI; + +/*------------------------------------------------------------------------------* + * Following are initialize/terminate functions for Router Information + *handling. + *------------------------------------------------------------------------------*/ + +static void ospf_router_info_ism_change(struct ospf_interface *oi, + int old_status); +static void ospf_router_info_config_write_router(struct vty *vty); +static void ospf_router_info_show_info(struct vty *vty, + struct json_object *json, + struct ospf_lsa *lsa); +static int ospf_router_info_lsa_originate(void *arg); +static struct ospf_lsa *ospf_router_info_lsa_refresh(struct ospf_lsa *lsa); +static void ospf_router_info_lsa_schedule(struct ospf_ri_area_info *ai, + enum lsa_opcode opcode); +static void ospf_router_info_register_vty(void); +static int ospf_router_info_lsa_update(struct ospf_lsa *lsa); +static void del_area_info(void *val); +static void del_pce_info(void *val); + +int ospf_router_info_init(void) +{ + + zlog_info("RI (%s): Initialize Router Information", __func__); + + memset(&OspfRI, 0, sizeof(OspfRI)); + OspfRI.enabled = false; + OspfRI.registered = 0; + OspfRI.scope = OSPF_OPAQUE_AS_LSA; + OspfRI.as_flags = RIFLG_LSA_INACTIVE; + OspfRI.area_info = list_new(); + OspfRI.area_info->del = del_area_info; + + /* Initialize pce domain and neighbor list */ + OspfRI.pce_info.enabled = false; + OspfRI.pce_info.pce_domain = list_new(); + OspfRI.pce_info.pce_domain->del = del_pce_info; + OspfRI.pce_info.pce_neighbor = list_new(); + OspfRI.pce_info.pce_neighbor->del = del_pce_info; + + /* Initialize Segment Routing information structure */ + OspfRI.sr_info.enabled = false; + + ospf_router_info_register_vty(); + + return 0; +} + +static int ospf_router_info_register(uint8_t scope) +{ + int rc = 0; + + if (OspfRI.registered) + return rc; + + zlog_info("RI (%s): Register Router Information with scope %s(%d)", + __func__, + scope == OSPF_OPAQUE_AREA_LSA ? "Area" : "AS", scope); + rc = ospf_register_opaque_functab( + scope, OPAQUE_TYPE_ROUTER_INFORMATION_LSA, + NULL, /* new interface */ + NULL, /* del interface */ + ospf_router_info_ism_change, + NULL, /* NSM change */ + ospf_router_info_config_write_router, + NULL, /* Config. write interface */ + NULL, /* Config. write debug */ + ospf_router_info_show_info, ospf_router_info_lsa_originate, + ospf_router_info_lsa_refresh, ospf_router_info_lsa_update, + NULL); /* del_lsa_hook */ + + if (rc != 0) { + flog_warn( + EC_OSPF_OPAQUE_REGISTRATION, + "RI (%s): Failed to register functions", __func__); + return rc; + } + + OspfRI.registered = 1; + OspfRI.scope = scope; + return rc; +} + +static int ospf_router_info_unregister(void) +{ + + if ((OspfRI.scope != OSPF_OPAQUE_AS_LSA) + && (OspfRI.scope != OSPF_OPAQUE_AREA_LSA)) { + assert("Unable to unregister Router Info functions: Wrong scope!" + == NULL); + return -1; + } + + ospf_delete_opaque_functab(OspfRI.scope, + OPAQUE_TYPE_ROUTER_INFORMATION_LSA); + + OspfRI.registered = 0; + return 0; +} + +void ospf_router_info_term(void) +{ + + list_delete(&OspfRI.pce_info.pce_domain); + list_delete(&OspfRI.pce_info.pce_neighbor); + + OspfRI.enabled = false; + + ospf_router_info_unregister(); + + return; +} + +void ospf_router_info_finish(void) +{ + struct listnode *node, *nnode; + struct ospf_ri_area_info *ai; + + /* Flush Router Info LSA */ + for (ALL_LIST_ELEMENTS(OspfRI.area_info, node, nnode, ai)) + if (CHECK_FLAG(ai->flags, RIFLG_LSA_ENGAGED)) + ospf_router_info_lsa_schedule(ai, FLUSH_THIS_LSA); + + list_delete_all_node(OspfRI.pce_info.pce_domain); + list_delete_all_node(OspfRI.pce_info.pce_neighbor); + + OspfRI.enabled = false; +} + +static void del_area_info(void *val) +{ + XFREE(MTYPE_OSPF_ROUTER_INFO, val); +} + +static void del_pce_info(void *val) +{ + XFREE(MTYPE_OSPF_PCE_PARAMS, val); +} + +/* Catch RI LSA flooding Scope for ospf_ext.[h,c] code */ +struct scope_info ospf_router_info_get_flooding_scope(void) +{ + struct scope_info flooding_scope; + + if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) { + flooding_scope.scope = OSPF_OPAQUE_AS_LSA; + flooding_scope.areas = NULL; + return flooding_scope; + } + flooding_scope.scope = OSPF_OPAQUE_AREA_LSA; + flooding_scope.areas = OspfRI.area_info; + return flooding_scope; +} + +static struct ospf_ri_area_info *lookup_by_area(struct ospf_area *area) +{ + struct listnode *node, *nnode; + struct ospf_ri_area_info *ai; + + for (ALL_LIST_ELEMENTS(OspfRI.area_info, node, nnode, ai)) + if (ai->area == area) + return ai; + + return NULL; +} + +/*------------------------------------------------------------------------* + * Following are control functions for ROUTER INFORMATION parameters + *management. + *------------------------------------------------------------------------*/ + +static void set_router_info_capabilities(struct ri_tlv_router_cap *ric, + uint32_t cap) +{ + ric->header.type = htons(RI_TLV_CAPABILITIES); + ric->header.length = htons(RI_TLV_LENGTH); + ric->value = htonl(cap); + return; +} + +static int set_pce_header(struct ospf_pce_info *pce) +{ + uint16_t length = 0; + struct listnode *node; + struct ri_pce_subtlv_domain *domain; + struct ri_pce_subtlv_neighbor *neighbor; + + /* PCE Address */ + if (ntohs(pce->pce_address.header.type) != 0) + length += TLV_SIZE(&pce->pce_address.header); + + /* PCE Path Scope */ + if (ntohs(pce->pce_scope.header.type) != 0) + length += TLV_SIZE(&pce->pce_scope.header); + + /* PCE Domain */ + for (ALL_LIST_ELEMENTS_RO(pce->pce_domain, node, domain)) { + if (ntohs(domain->header.type) != 0) + length += TLV_SIZE(&domain->header); + } + + /* PCE Neighbor */ + for (ALL_LIST_ELEMENTS_RO(pce->pce_neighbor, node, neighbor)) { + if (ntohs(neighbor->header.type) != 0) + length += TLV_SIZE(&neighbor->header); + } + + /* PCE Capabilities */ + if (ntohs(pce->pce_cap_flag.header.type) != 0) + length += TLV_SIZE(&pce->pce_cap_flag.header); + + if (length != 0) { + pce->pce_header.header.type = htons(RI_TLV_PCE); + pce->pce_header.header.length = htons(length); + pce->enabled = true; + } else { + pce->pce_header.header.type = 0; + pce->pce_header.header.length = 0; + pce->enabled = false; + } + + return length; +} + +static void set_pce_address(struct in_addr ipv4, struct ospf_pce_info *pce) +{ + + /* Enable PCE Info */ + pce->pce_header.header.type = htons(RI_TLV_PCE); + /* Set PCE Address */ + pce->pce_address.header.type = htons(RI_PCE_SUBTLV_ADDRESS); + pce->pce_address.header.length = htons(PCE_ADDRESS_IPV4_SIZE); + pce->pce_address.address.type = htons(PCE_ADDRESS_IPV4); + pce->pce_address.address.value = ipv4; + + return; +} + +static void set_pce_path_scope(uint32_t scope, struct ospf_pce_info *pce) +{ + + /* Set PCE Scope */ + pce->pce_scope.header.type = htons(RI_PCE_SUBTLV_PATH_SCOPE); + pce->pce_scope.header.length = htons(RI_TLV_LENGTH); + pce->pce_scope.value = htonl(scope); + + return; +} + +static void set_pce_domain(uint16_t type, uint32_t domain, + struct ospf_pce_info *pce) +{ + + struct ri_pce_subtlv_domain *new; + + /* Create new domain info */ + new = XCALLOC(MTYPE_OSPF_PCE_PARAMS, + sizeof(struct ri_pce_subtlv_domain)); + + new->header.type = htons(RI_PCE_SUBTLV_DOMAIN); + new->header.length = htons(PCE_ADDRESS_IPV4_SIZE); + new->type = htons(type); + new->value = htonl(domain); + + /* Add new domain to the list */ + listnode_add(pce->pce_domain, new); + + return; +} + +static void unset_pce_domain(uint16_t type, uint32_t domain, + struct ospf_pce_info *pce) +{ + struct listnode *node; + struct ri_pce_subtlv_domain *old = NULL; + int found = 0; + + /* Search the corresponding node */ + for (ALL_LIST_ELEMENTS_RO(pce->pce_domain, node, old)) { + if ((old->type == htons(type)) + && (old->value == htonl(domain))) { + found = 1; + break; + } + } + + /* if found remove it */ + if (found) { + listnode_delete(pce->pce_domain, old); + + /* Finally free the old domain */ + XFREE(MTYPE_OSPF_PCE_PARAMS, old); + } +} + +static void set_pce_neighbor(uint16_t type, uint32_t domain, + struct ospf_pce_info *pce) +{ + + struct ri_pce_subtlv_neighbor *new; + + /* Create new neighbor info */ + new = XCALLOC(MTYPE_OSPF_PCE_PARAMS, + sizeof(struct ri_pce_subtlv_neighbor)); + + new->header.type = htons(RI_PCE_SUBTLV_NEIGHBOR); + new->header.length = htons(PCE_ADDRESS_IPV4_SIZE); + new->type = htons(type); + new->value = htonl(domain); + + /* Add new domain to the list */ + listnode_add(pce->pce_neighbor, new); + + return; +} + +static void unset_pce_neighbor(uint16_t type, uint32_t domain, + struct ospf_pce_info *pce) +{ + struct listnode *node; + struct ri_pce_subtlv_neighbor *old = NULL; + int found = 0; + + /* Search the corresponding node */ + for (ALL_LIST_ELEMENTS_RO(pce->pce_neighbor, node, old)) { + if ((old->type == htons(type)) + && (old->value == htonl(domain))) { + found = 1; + break; + } + } + + /* if found remove it */ + if (found) { + listnode_delete(pce->pce_neighbor, old); + + /* Finally free the old domain */ + XFREE(MTYPE_OSPF_PCE_PARAMS, old); + } +} + +static void set_pce_cap_flag(uint32_t cap, struct ospf_pce_info *pce) +{ + + /* Set PCE Capabilities flag */ + pce->pce_cap_flag.header.type = htons(RI_PCE_SUBTLV_CAP_FLAG); + pce->pce_cap_flag.header.length = htons(RI_TLV_LENGTH); + pce->pce_cap_flag.value = htonl(cap); + + return; +} + +/* Segment Routing TLV setter */ + +/* Algorithm SubTLV - section 3.1 */ +static void set_sr_algorithm(uint8_t algo) +{ + + OspfRI.sr_info.algo.value[0] = algo; + for (int i = 1; i < ALGORITHM_COUNT; i++) + OspfRI.sr_info.algo.value[i] = SR_ALGORITHM_UNSET; + + /* Set TLV type and length == only 1 Algorithm */ + TLV_TYPE(OspfRI.sr_info.algo) = htons(RI_SR_TLV_SR_ALGORITHM); + TLV_LEN(OspfRI.sr_info.algo) = htons(sizeof(uint8_t)); +} + +/* unset Aglogithm SubTLV */ +static void unset_sr_algorithm(uint8_t algo) +{ + + for (int i = 0; i < ALGORITHM_COUNT; i++) + OspfRI.sr_info.algo.value[i] = SR_ALGORITHM_UNSET; + + /* Unset TLV type and length */ + TLV_TYPE(OspfRI.sr_info.algo) = htons(0); + TLV_LEN(OspfRI.sr_info.algo) = htons(0); +} + +/* Set Segment Routing Global Block SubTLV - section 3.2 */ +static void set_sr_global_label_range(struct sr_block srgb) +{ + /* Set Header */ + TLV_TYPE(OspfRI.sr_info.srgb) = htons(RI_SR_TLV_SRGB_LABEL_RANGE); + TLV_LEN(OspfRI.sr_info.srgb) = htons(RI_SR_TLV_LABEL_RANGE_SIZE); + /* Set Range Size */ + OspfRI.sr_info.srgb.size = htonl(SET_RANGE_SIZE(srgb.range_size)); + /* Set Lower bound label SubTLV */ + TLV_TYPE(OspfRI.sr_info.srgb.lower) = htons(SUBTLV_SID_LABEL); + TLV_LEN(OspfRI.sr_info.srgb.lower) = htons(SID_RANGE_LABEL_LENGTH); + OspfRI.sr_info.srgb.lower.value = htonl(SET_LABEL(srgb.lower_bound)); +} + +/* Unset Segment Routing Global Block SubTLV */ +static void unset_sr_global_label_range(void) +{ + TLV_TYPE(OspfRI.sr_info.srgb) = htons(0); + TLV_LEN(OspfRI.sr_info.srgb) = htons(0); + TLV_TYPE(OspfRI.sr_info.srgb.lower) = htons(0); + TLV_LEN(OspfRI.sr_info.srgb.lower) = htons(0); +} + +/* Set Segment Routing Local Block SubTLV - section 3.2 */ +static void set_sr_local_label_range(struct sr_block srlb) +{ + /* Set Header */ + TLV_TYPE(OspfRI.sr_info.srlb) = htons(RI_SR_TLV_SRLB_LABEL_RANGE); + TLV_LEN(OspfRI.sr_info.srlb) = htons(RI_SR_TLV_LABEL_RANGE_SIZE); + /* Set Range Size */ + OspfRI.sr_info.srlb.size = htonl(SET_RANGE_SIZE(srlb.range_size)); + /* Set Lower bound label SubTLV */ + TLV_TYPE(OspfRI.sr_info.srlb.lower) = htons(SUBTLV_SID_LABEL); + TLV_LEN(OspfRI.sr_info.srlb.lower) = htons(SID_RANGE_LABEL_LENGTH); + OspfRI.sr_info.srlb.lower.value = htonl(SET_LABEL(srlb.lower_bound)); +} + +/* Unset Segment Routing Local Block SubTLV */ +static void unset_sr_local_label_range(void) +{ + TLV_TYPE(OspfRI.sr_info.srlb) = htons(0); + TLV_LEN(OspfRI.sr_info.srlb) = htons(0); + TLV_TYPE(OspfRI.sr_info.srlb.lower) = htons(0); + TLV_LEN(OspfRI.sr_info.srlb.lower) = htons(0); +} + +/* Set Maximum Stack Depth for this router */ +static void set_sr_node_msd(uint8_t msd) +{ + TLV_TYPE(OspfRI.sr_info.msd) = htons(RI_SR_TLV_NODE_MSD); + TLV_LEN(OspfRI.sr_info.msd) = htons(sizeof(uint32_t)); + OspfRI.sr_info.msd.value = msd; +} + +/* Unset this router MSD */ +static void unset_sr_node_msd(void) +{ + TLV_TYPE(OspfRI.sr_info.msd) = htons(0); + TLV_LEN(OspfRI.sr_info.msd) = htons(0); +} + +static void unset_param(void *tlv_buffer) +{ + struct tlv_header *tlv = (struct tlv_header *)tlv_buffer; + + tlv->type = 0; + /* Fill the Value to 0 */ + memset(TLV_DATA(tlv_buffer), 0, TLV_BODY_SIZE(tlv)); + tlv->length = 0; + + return; +} + +static void initialize_params(struct ospf_router_info *ori) +{ + uint32_t cap = 0; + struct ospf *top; + struct listnode *node, *nnode; + struct ospf_area *area; + struct ospf_ri_area_info *new; + + /* + * Initialize default Router Information Capabilities. + */ + cap = RI_TE_SUPPORT; + + set_router_info_capabilities(&ori->router_cap, cap); + + /* If Area address is not null and exist, retrieve corresponding + * structure */ + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + zlog_info("RI (%s): Initialize Router Info for %s scope", __func__, + OspfRI.scope == OSPF_OPAQUE_AREA_LSA ? "Area" : "AS"); + + /* Try to get available Area's context from ospf at this step. + * Do it latter if not available */ + if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA) { + if (!list_isempty(OspfRI.area_info)) + list_delete_all_node(OspfRI.area_info); + for (ALL_LIST_ELEMENTS(top->areas, node, nnode, area)) { + zlog_debug("RI (%s): Add area %pI4 to Router Information", + __func__, &area->area_id); + new = XCALLOC(MTYPE_OSPF_ROUTER_INFO, + sizeof(struct ospf_ri_area_info)); + new->area = area; + new->flags = RIFLG_LSA_INACTIVE; + listnode_add(OspfRI.area_info, new); + } + } + + /* + * Initialize default PCE Information values + */ + /* PCE address == OSPF Router ID */ + set_pce_address(top->router_id, &ori->pce_info); + + /* PCE scope */ + cap = 7; /* Set L, R and Rd bits to one = intra & inter-area path + computation */ + set_pce_path_scope(cap, &ori->pce_info); + + /* PCE Capabilities */ + cap = PCE_CAP_BIDIRECTIONAL | PCE_CAP_DIVERSE_PATH | PCE_CAP_OBJECTIVES + | PCE_CAP_ADDITIVE | PCE_CAP_MULTIPLE_REQ; + set_pce_cap_flag(cap, &ori->pce_info); + + return; +} + +static int is_mandated_params_set(struct ospf_router_info *ori) +{ + int rc = 0; + + if (ori == NULL) + return rc; + + if (ntohs(ori->router_cap.header.type) == 0) + return rc; + + if ((ntohs(ori->pce_info.pce_header.header.type) == RI_TLV_PCE) + && (ntohs(ori->pce_info.pce_address.header.type) == 0) + && (ntohs(ori->pce_info.pce_cap_flag.header.type) == 0)) + return rc; + + if ((ori->sr_info.enabled) && (ntohs(TLV_TYPE(ori->sr_info.algo)) == 0) + && (ntohs(TLV_TYPE(ori->sr_info.srgb)) == 0)) + return rc; + + rc = 1; + + return rc; +} + +/* + * Used by Segment Routing to set new TLVs and Sub-TLVs values + * + * @param enable To activate or not Segment Routing router Information flooding + * @param srn Self Segment Routing node + * + * @return none + */ +void ospf_router_info_update_sr(bool enable, struct sr_node *srn) +{ + struct listnode *node, *nnode; + struct ospf_ri_area_info *ai; + + /* First, check if Router Information is registered or not */ + if (!OspfRI.registered) + ospf_router_info_register(OSPF_OPAQUE_AREA_LSA); + + /* Verify that scope is AREA */ + if (OspfRI.scope != OSPF_OPAQUE_AREA_LSA) { + zlog_err( + "RI (%s): Router Info is %s flooding: Change scope to Area flooding for Segment Routing", + __func__, + OspfRI.scope == OSPF_OPAQUE_AREA_LSA ? "Area" : "AS"); + return; + } + + /* Then, activate and initialize Router Information if necessary */ + if (!OspfRI.enabled) { + OspfRI.enabled = true; + initialize_params(&OspfRI); + } + + /* Check that SR node is valid */ + if (srn == NULL) + return; + + if (IS_DEBUG_OSPF_SR) + zlog_debug("RI (%s): %s Routing Information for Segment Routing", + __func__, enable ? "Enable" : "Disable"); + + /* Unset or Set SR parameters */ + if (!enable) { + unset_sr_algorithm(SR_ALGORITHM_SPF); + unset_sr_global_label_range(); + unset_sr_local_label_range(); + unset_sr_node_msd(); + OspfRI.sr_info.enabled = false; + } else { + // Only SR_ALGORITHM_SPF is supported + set_sr_algorithm(SR_ALGORITHM_SPF); + set_sr_global_label_range(srn->srgb); + set_sr_local_label_range(srn->srlb); + if (srn->msd != 0) + set_sr_node_msd(srn->msd); + else + unset_sr_node_msd(); + OspfRI.sr_info.enabled = true; + } + + /* Refresh if already engaged or originate RI LSA */ + for (ALL_LIST_ELEMENTS(OspfRI.area_info, node, nnode, ai)) { + if (CHECK_FLAG(ai->flags, RIFLG_LSA_ENGAGED)) + ospf_router_info_lsa_schedule(ai, REFRESH_THIS_LSA); + else + ospf_router_info_lsa_schedule(ai, + REORIGINATE_THIS_LSA); + + } +} + +/*------------------------------------------------------------------------* + * Following are callback functions against generic Opaque-LSAs handling. + *------------------------------------------------------------------------*/ +static void ospf_router_info_ism_change(struct ospf_interface *oi, + int old_state) +{ + + struct ospf_ri_area_info *ai; + + /* Collect area information */ + ai = lookup_by_area(oi->area); + + /* Check if area is not yet registered */ + if (ai != NULL) + return; + + /* Add this new area to the list */ + ai = XCALLOC(MTYPE_OSPF_ROUTER_INFO, sizeof(struct ospf_ri_area_info)); + ai->area = oi->area; + ai->flags = RIFLG_LSA_INACTIVE; + listnode_add(OspfRI.area_info, ai); + + return; +} + +/*------------------------------------------------------------------------* + * Following are OSPF protocol processing functions for ROUTER INFORMATION + *------------------------------------------------------------------------*/ + +static void build_tlv_header(struct stream *s, struct tlv_header *tlvh) +{ + + stream_put(s, tlvh, sizeof(struct tlv_header)); + return; +} + +static void build_tlv(struct stream *s, struct tlv_header *tlvh) +{ + + if (ntohs(tlvh->type) != 0) { + build_tlv_header(s, tlvh); + stream_put(s, TLV_DATA(tlvh), TLV_BODY_SIZE(tlvh)); + } + return; +} + +static void ospf_router_info_lsa_body_set(struct stream *s) +{ + + struct listnode *node; + struct ri_pce_subtlv_domain *domain; + struct ri_pce_subtlv_neighbor *neighbor; + + /* Build Router Information TLV */ + build_tlv(s, &OspfRI.router_cap.header); + + /* Build Segment Routing TLVs if enabled */ + if (OspfRI.sr_info.enabled) { + /* Build Algorithm TLV */ + build_tlv(s, &TLV_HDR(OspfRI.sr_info.algo)); + /* Build SRGB TLV */ + build_tlv(s, &TLV_HDR(OspfRI.sr_info.srgb)); + /* Build SRLB TLV */ + build_tlv(s, &TLV_HDR(OspfRI.sr_info.srlb)); + /* Build MSD TLV */ + build_tlv(s, &TLV_HDR(OspfRI.sr_info.msd)); + } + + /* Add RI PCE TLV if it is set */ + if (OspfRI.pce_info.enabled) { + + /* Compute PCE Info header first */ + set_pce_header(&OspfRI.pce_info); + + /* Build PCE TLV */ + build_tlv_header(s, &OspfRI.pce_info.pce_header.header); + + /* Build PCE address sub-tlv */ + build_tlv(s, &OspfRI.pce_info.pce_address.header); + + /* Build PCE path scope sub-tlv */ + build_tlv(s, &OspfRI.pce_info.pce_scope.header); + + /* Build PCE domain sub-tlv */ + for (ALL_LIST_ELEMENTS_RO(OspfRI.pce_info.pce_domain, node, + domain)) + build_tlv(s, &domain->header); + + /* Build PCE neighbor sub-tlv */ + for (ALL_LIST_ELEMENTS_RO(OspfRI.pce_info.pce_neighbor, node, + neighbor)) + build_tlv(s, &neighbor->header); + + /* Build PCE cap flag sub-tlv */ + build_tlv(s, &OspfRI.pce_info.pce_cap_flag.header); + } + + return; +} + +/* Create new opaque-LSA. */ +static struct ospf_lsa *ospf_router_info_lsa_new(struct ospf_area *area) +{ + struct ospf *top; + struct stream *s; + struct lsa_header *lsah; + struct ospf_lsa *new = NULL; + uint8_t options, lsa_type; + struct in_addr lsa_id; + uint32_t tmp; + uint16_t length; + + /* Create a stream for LSA. */ + s = stream_new(OSPF_MAX_LSA_SIZE); + + lsah = (struct lsa_header *)STREAM_DATA(s); + + options = OSPF_OPTION_E; /* Enable AS external as we flood RI with + Opaque Type 11 */ + options |= OSPF_OPTION_O; /* Don't forget this :-) */ + + lsa_type = OspfRI.scope; + /* LSA ID == 0 for Router Information see RFC 4970 */ + tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_ROUTER_INFORMATION_LSA, 0); + lsa_id.s_addr = htonl(tmp); + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type%d:%pI4]: Create an Opaque-LSA/ROUTER INFORMATION instance", + lsa_type, &lsa_id); + + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + /* Set opaque-LSA header fields. */ + lsa_header_set(s, options, lsa_type, lsa_id, top->router_id); + + /* Set opaque-LSA body fields. */ + ospf_router_info_lsa_body_set(s); + + /* Set length. */ + length = stream_get_endp(s); + lsah->length = htons(length); + + /* Now, create an OSPF LSA instance. */ + new = ospf_lsa_new_and_data(length); + + /* Routing Information is only supported for default VRF */ + new->vrf_id = VRF_DEFAULT; + new->area = area; + + SET_FLAG(new->flags, OSPF_LSA_SELF); + memcpy(new->data, lsah, length); + stream_free(s); + + return new; +} + +static int ospf_router_info_lsa_originate_as(void *arg) +{ + struct ospf_lsa *new; + struct ospf *top; + int rc = -1; + + /* Sanity Check */ + if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA) { + flog_warn( + EC_OSPF_LSA_INSTALL_FAILURE, + "RI (%s): wrong flooding scope AREA instead of AS ?", + __func__); + return rc; + } + + /* Create new Opaque-LSA/ROUTER INFORMATION instance. */ + new = ospf_router_info_lsa_new(NULL); + top = (struct ospf *)arg; + + /* Check ospf info */ + if (top == NULL) { + zlog_debug("RI (%s): ospf instance not found for vrf id %u", + __func__, VRF_DEFAULT); + ospf_lsa_unlock(&new); + return rc; + } + + /* Install this LSA into LSDB. */ + if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) { + flog_warn( + EC_OSPF_LSA_INSTALL_FAILURE, + "RI (%s): ospf_lsa_install() ?", __func__); + ospf_lsa_unlock(&new); + return rc; + } + + /* Update new LSA origination count. */ + top->lsa_originate_count++; + + /* Flood new LSA through AREA or AS. */ + SET_FLAG(OspfRI.as_flags, RIFLG_LSA_ENGAGED); + ospf_flood_through_as(top, NULL /*nbr */, new); + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug( + "LSA[Type%d:%pI4]: Originate Opaque-LSA/ROUTER INFORMATION", + new->data->type, &new->data->id); + ospf_lsa_header_dump(new->data); + } + + rc = 0; + return rc; +} + +static int ospf_router_info_lsa_originate_area(void *arg) +{ + struct ospf_lsa *new; + struct ospf *top; + struct ospf_ri_area_info *ai = NULL; + int rc = -1; + + /* Sanity Check */ + if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) { + flog_warn( + EC_OSPF_LSA_INSTALL_FAILURE, + "RI (%s): wrong flooding scope AS instead of AREA ?", + __func__); + return rc; + } + + /* Create new Opaque-LSA/ROUTER INFORMATION instance. */ + ai = lookup_by_area((struct ospf_area *)arg); + if (ai == NULL) { + zlog_debug( + "RI (%s): There is no context for this Router Information. Stop processing", + __func__); + return rc; + } + + if (ai->area->ospf) + top = ai->area->ospf; + else + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + new = ospf_router_info_lsa_new(ai->area); + + /* Check ospf info */ + if (top == NULL) { + zlog_debug("RI (%s): ospf instance not found for vrf id %u", + __func__, VRF_DEFAULT); + ospf_lsa_unlock(&new); + return rc; + } + + /* Install this LSA into LSDB. */ + if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) { + flog_warn( + EC_OSPF_LSA_INSTALL_FAILURE, + "RI (%s): ospf_lsa_install() ?", __func__); + ospf_lsa_unlock(&new); + return rc; + } + + /* Update new LSA origination count. */ + top->lsa_originate_count++; + + /* Flood new LSA through AREA or AS. */ + SET_FLAG(ai->flags, RIFLG_LSA_ENGAGED); + ospf_flood_through_area(ai->area, NULL /*nbr */, new); + + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug( + "LSA[Type%d:%pI4]: Originate Opaque-LSA/ROUTER INFORMATION", + new->data->type, &new->data->id); + ospf_lsa_header_dump(new->data); + } + + rc = 0; + return rc; +} + +static int ospf_router_info_lsa_originate(void *arg) +{ + + struct ospf_ri_area_info *ai; + int rc = -1; + + if (!OspfRI.enabled) { + zlog_info("RI (%s): ROUTER INFORMATION is disabled now.", + __func__); + rc = 0; /* This is not an error case. */ + return rc; + } + + /* Check if Router Information LSA is already engaged */ + if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) { + if ((CHECK_FLAG(OspfRI.as_flags, RIFLG_LSA_ENGAGED)) + && (CHECK_FLAG(OspfRI.as_flags, + RIFLG_LSA_FORCED_REFRESH))) { + UNSET_FLAG(OspfRI.as_flags, RIFLG_LSA_FORCED_REFRESH); + ospf_router_info_lsa_schedule(NULL, REFRESH_THIS_LSA); + rc = 0; + return rc; + } + } else { + ai = lookup_by_area((struct ospf_area *)arg); + if (ai == NULL) { + flog_warn( + EC_OSPF_LSA, + "RI (%s): Missing area information", __func__); + return rc; + } + if ((CHECK_FLAG(ai->flags, RIFLG_LSA_ENGAGED)) + && (CHECK_FLAG(ai->flags, RIFLG_LSA_FORCED_REFRESH))) { + UNSET_FLAG(ai->flags, RIFLG_LSA_FORCED_REFRESH); + ospf_router_info_lsa_schedule(ai, REFRESH_THIS_LSA); + rc = 0; + return rc; + } + } + + /* Router Information is not yet Engaged, check parameters */ + if (!is_mandated_params_set(&OspfRI)) + flog_warn( + EC_OSPF_LSA, + "RI (%s): lacks mandated ROUTER INFORMATION parameters", + __func__); + + /* Ok, let's try to originate an LSA */ + if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) + rc = ospf_router_info_lsa_originate_as(arg); + else + rc = ospf_router_info_lsa_originate_area(arg); + + return rc; +} + +static struct ospf_lsa *ospf_router_info_lsa_refresh(struct ospf_lsa *lsa) +{ + struct ospf_ri_area_info *ai = NULL; + struct ospf_lsa *new = NULL; + struct ospf *top; + + if (!OspfRI.enabled) { + /* + * This LSA must have flushed before due to ROUTER INFORMATION + * status change. + * It seems a slip among routers in the routing domain. + */ + zlog_info("RI (%s): ROUTER INFORMATION is disabled now.", + __func__); + lsa->data->ls_age = + htons(OSPF_LSA_MAXAGE); /* Flush it anyway. */ + } + + /* Verify that the Router Information ID is supported */ + if (GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr)) != 0) { + flog_warn( + EC_OSPF_LSA, + "RI (%s): Unsupported Router Information ID", + __func__); + return NULL; + } + + /* Process LSA depending of the flooding scope */ + if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA) { + /* Get context AREA context */ + ai = lookup_by_area(lsa->area); + if (ai == NULL) { + flog_warn( + EC_OSPF_LSA, + "RI (%s): No associated Area", __func__); + return NULL; + } + /* Flush LSA, if the lsa's age reached to MaxAge. */ + if (IS_LSA_MAXAGE(lsa)) { + UNSET_FLAG(ai->flags, RIFLG_LSA_ENGAGED); + ospf_opaque_lsa_flush_schedule(lsa); + return NULL; + } + /* Create new Opaque-LSA/ROUTER INFORMATION instance. */ + new = ospf_router_info_lsa_new(ai->area); + new->data->ls_seqnum = lsa_seqnum_increment(lsa); + /* Install this LSA into LSDB. */ + /* Given "lsa" will be freed in the next function. */ + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) { + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "RI (%s): ospf_lsa_install() ?", __func__); + ospf_lsa_unlock(&new); + return new; + } + /* Flood updated LSA through AREA */ + ospf_flood_through_area(ai->area, NULL /*nbr */, new); + + } else { /* AS Flooding scope */ + /* Flush LSA, if the lsa's age reached to MaxAge. */ + if (IS_LSA_MAXAGE(lsa)) { + UNSET_FLAG(OspfRI.as_flags, RIFLG_LSA_ENGAGED); + ospf_opaque_lsa_flush_schedule(lsa); + return NULL; + } + /* Create new Opaque-LSA/ROUTER INFORMATION instance. */ + new = ospf_router_info_lsa_new(NULL); + new->data->ls_seqnum = lsa_seqnum_increment(lsa); + /* Install this LSA into LSDB. */ + /* Given "lsa" will be freed in the next function. */ + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) { + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "RI (%s): ospf_lsa_install() ?", __func__); + ospf_lsa_unlock(&new); + return new; + } + /* Flood updated LSA through AS */ + ospf_flood_through_as(top, NULL /*nbr */, new); + } + + /* Debug logging. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug( + "LSA[Type%d:%pI4]: Refresh Opaque-LSA/ROUTER INFORMATION", + new->data->type, &new->data->id); + ospf_lsa_header_dump(new->data); + } + + return new; +} + +static void ospf_router_info_lsa_schedule(struct ospf_ri_area_info *ai, + enum lsa_opcode opcode) +{ + struct ospf_lsa lsa; + struct lsa_header lsah; + struct ospf *top; + uint32_t tmp; + + memset(&lsa, 0, sizeof(lsa)); + memset(&lsah, 0, sizeof(lsah)); + + zlog_debug("RI (%s): LSA schedule %s%s%s", __func__, + opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "", + opcode == REFRESH_THIS_LSA ? "Refresh" : "", + opcode == FLUSH_THIS_LSA ? "Flush" : ""); + + /* Check LSA flags state coherence and collect area information */ + if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA) { + if ((ai == NULL) || (ai->area == NULL)) { + flog_warn( + EC_OSPF_LSA, + "RI (%s): Router Info is Area scope flooding but area is not set", + __func__); + return; + } + + if (!CHECK_FLAG(ai->flags, RIFLG_LSA_ENGAGED) + && (opcode != REORIGINATE_THIS_LSA)) + return; + + if (CHECK_FLAG(ai->flags, RIFLG_LSA_ENGAGED) + && (opcode == REORIGINATE_THIS_LSA)) + opcode = REFRESH_THIS_LSA; + + lsa.area = ai->area; + top = ai->area->ospf; + } else { + if (!CHECK_FLAG(OspfRI.as_flags, RIFLG_LSA_ENGAGED) + && (opcode != REORIGINATE_THIS_LSA)) + return; + + if (CHECK_FLAG(OspfRI.as_flags, RIFLG_LSA_ENGAGED) + && (opcode == REORIGINATE_THIS_LSA)) + opcode = REFRESH_THIS_LSA; + + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + lsa.area = NULL; + } + + lsa.data = &lsah; + lsah.type = OspfRI.scope; + + /* LSA ID is set to 0 for the Router Information. See RFC 4970 */ + tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_ROUTER_INFORMATION_LSA, 0); + lsah.id.s_addr = htonl(tmp); + + switch (opcode) { + case REORIGINATE_THIS_LSA: + if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA) + ospf_opaque_lsa_reoriginate_schedule( + (void *)ai->area, OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_ROUTER_INFORMATION_LSA); + else + ospf_opaque_lsa_reoriginate_schedule( + (void *)top, OSPF_OPAQUE_AS_LSA, + OPAQUE_TYPE_ROUTER_INFORMATION_LSA); + break; + case REFRESH_THIS_LSA: + ospf_opaque_lsa_refresh_schedule(&lsa); + break; + case FLUSH_THIS_LSA: + if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA) + UNSET_FLAG(ai->flags, RIFLG_LSA_ENGAGED); + else + UNSET_FLAG(OspfRI.as_flags, RIFLG_LSA_ENGAGED); + ospf_opaque_lsa_flush_schedule(&lsa); + break; + } + + return; +} + +/* Callback to handle Segment Routing information */ +static int ospf_router_info_lsa_update(struct ospf_lsa *lsa) +{ + + /* Sanity Check */ + if (lsa == NULL) { + flog_warn(EC_OSPF_LSA, "RI (%s): Abort! LSA is NULL", + __func__); + return -1; + } + + /* Process only Opaque LSA */ + if ((lsa->data->type != OSPF_OPAQUE_AREA_LSA) + && (lsa->data->type != OSPF_OPAQUE_AS_LSA)) + return 0; + + /* Process only Router Information LSA */ + if (GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)) + != OPAQUE_TYPE_ROUTER_INFORMATION_LSA) + return 0; + + /* Check if it is not my LSA */ + if (IS_LSA_SELF(lsa)) + return 0; + + /* Check if Router Info & Segment Routing are enable */ + if (!OspfRI.enabled || !OspfRI.sr_info.enabled) + return 0; + + /* Call Segment Routing LSA update or deletion */ + if (!IS_LSA_MAXAGE(lsa)) + ospf_sr_ri_lsa_update(lsa); + else + ospf_sr_ri_lsa_delete(lsa); + + return 0; +} + +/*------------------------------------------------------------------------* + * Following are vty session control functions. + *------------------------------------------------------------------------*/ + +#define check_tlv_size(size, msg) \ + do { \ + if (ntohs(tlvh->length) > size) { \ + if (vty != NULL) \ + vty_out(vty, " Wrong %s TLV size: %d(%d)\n", \ + msg, ntohs(tlvh->length), size); \ + else \ + zlog_debug(" Wrong %s TLV size: %d(%d)", \ + msg, ntohs(tlvh->length), size); \ + return size + TLV_HDR_SIZE; \ + } \ + } while (0) + +static uint16_t show_vty_router_cap(struct vty *vty, struct tlv_header *tlvh) +{ + struct ri_tlv_router_cap *top = (struct ri_tlv_router_cap *)tlvh; + + check_tlv_size(RI_TLV_CAPABILITIES_SIZE, "Router Capabilities"); + + if (vty != NULL) + vty_out(vty, " Router Capabilities: 0x%x\n", + ntohl(top->value)); + else + zlog_debug(" Router Capabilities: 0x%x", ntohl(top->value)); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_pce_subtlv_address(struct vty *vty, + struct tlv_header *tlvh) +{ + struct ri_pce_subtlv_address *top = + (struct ri_pce_subtlv_address *)tlvh; + + if (ntohs(top->address.type) == PCE_ADDRESS_IPV4) { + check_tlv_size(PCE_ADDRESS_IPV4_SIZE, "PCE Address"); + if (vty != NULL) + vty_out(vty, " PCE Address: %pI4\n", + &top->address.value); + else + zlog_debug(" PCE Address: %pI4", + &top->address.value); + } else if (ntohs(top->address.type) == PCE_ADDRESS_IPV6) { + /* TODO: Add support to IPv6 with inet_ntop() */ + check_tlv_size(PCE_ADDRESS_IPV6_SIZE, "PCE Address"); + if (vty != NULL) + vty_out(vty, " PCE Address: 0x%x\n", + ntohl(top->address.value.s_addr)); + else + zlog_debug(" PCE Address: 0x%x", + ntohl(top->address.value.s_addr)); + } else { + if (vty != NULL) + vty_out(vty, " Wrong PCE Address type: 0x%x\n", + ntohl(top->address.type)); + else + zlog_debug(" Wrong PCE Address type: 0x%x", + ntohl(top->address.type)); + } + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_pce_subtlv_path_scope(struct vty *vty, + struct tlv_header *tlvh) +{ + struct ri_pce_subtlv_path_scope *top = + (struct ri_pce_subtlv_path_scope *)tlvh; + + check_tlv_size(RI_PCE_SUBTLV_PATH_SCOPE_SIZE, "PCE Path Scope"); + + if (vty != NULL) + vty_out(vty, " PCE Path Scope: 0x%x\n", ntohl(top->value)); + else + zlog_debug(" PCE Path Scope: 0x%x", ntohl(top->value)); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_pce_subtlv_domain(struct vty *vty, + struct tlv_header *tlvh) +{ + struct ri_pce_subtlv_domain *top = (struct ri_pce_subtlv_domain *)tlvh; + struct in_addr tmp; + + check_tlv_size(RI_PCE_SUBTLV_DOMAIN_SIZE, "PCE Domain"); + + if (ntohs(top->type) == PCE_DOMAIN_TYPE_AREA) { + tmp.s_addr = top->value; + if (vty != NULL) + vty_out(vty, " PCE Domain Area: %pI4\n", &tmp); + else + zlog_debug(" PCE Domain Area: %pI4", &tmp); + } else if (ntohs(top->type) == PCE_DOMAIN_TYPE_AS) { + if (vty != NULL) + vty_out(vty, " PCE Domain AS: %d\n", + ntohl(top->value)); + else + zlog_debug(" PCE Domain AS: %d", ntohl(top->value)); + } else { + if (vty != NULL) + vty_out(vty, " Wrong PCE Domain type: %d\n", + ntohl(top->type)); + else + zlog_debug(" Wrong PCE Domain type: %d", + ntohl(top->type)); + } + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_pce_subtlv_neighbor(struct vty *vty, + struct tlv_header *tlvh) +{ + + struct ri_pce_subtlv_neighbor *top = + (struct ri_pce_subtlv_neighbor *)tlvh; + struct in_addr tmp; + + check_tlv_size(RI_PCE_SUBTLV_NEIGHBOR_SIZE, "PCE Neighbor"); + + if (ntohs(top->type) == PCE_DOMAIN_TYPE_AREA) { + tmp.s_addr = top->value; + if (vty != NULL) + vty_out(vty, " PCE Neighbor Area: %pI4\n", &tmp); + else + zlog_debug(" PCE Neighbor Area: %pI4", &tmp); + } else if (ntohs(top->type) == PCE_DOMAIN_TYPE_AS) { + if (vty != NULL) + vty_out(vty, " PCE Neighbor AS: %d\n", + ntohl(top->value)); + else + zlog_debug(" PCE Neighbor AS: %d", + ntohl(top->value)); + } else { + if (vty != NULL) + vty_out(vty, " Wrong PCE Neighbor type: %d\n", + ntohl(top->type)); + else + zlog_debug(" Wrong PCE Neighbor type: %d", + ntohl(top->type)); + } + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_pce_subtlv_cap_flag(struct vty *vty, + struct tlv_header *tlvh) +{ + struct ri_pce_subtlv_cap_flag *top = + (struct ri_pce_subtlv_cap_flag *)tlvh; + + check_tlv_size(RI_PCE_SUBTLV_CAP_FLAG_SIZE, "PCE Capabilities"); + + if (vty != NULL) + vty_out(vty, " PCE Capabilities Flag: 0x%x\n", + ntohl(top->value)); + else + zlog_debug(" PCE Capabilities Flag: 0x%x", + ntohl(top->value)); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_unknown_tlv(struct vty *vty, struct tlv_header *tlvh, + size_t buf_size) +{ + if (TLV_SIZE(tlvh) > buf_size) { + if (vty != NULL) + vty_out(vty, + " TLV size %d exceeds buffer size. Abort!", + TLV_SIZE(tlvh)); + else + zlog_debug( + " TLV size %d exceeds buffer size. Abort!", + TLV_SIZE(tlvh)); + return buf_size; + } + + if (vty != NULL) + vty_out(vty, " Unknown TLV: [type(0x%x), length(0x%x)]\n", + ntohs(tlvh->type), ntohs(tlvh->length)); + else + zlog_debug(" Unknown TLV: [type(0x%x), length(0x%x)]", + ntohs(tlvh->type), ntohs(tlvh->length)); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_pce_info(struct vty *vty, struct tlv_header *ri, + size_t buf_size) +{ + struct tlv_header *tlvh; + uint16_t length = ntohs(ri->length); + uint16_t sum = 0; + + /* Verify that TLV length is valid against remaining buffer size */ + if (length > buf_size) { + vty_out(vty, + " PCE Info TLV size %d exceeds buffer size. Abort!\n", + length); + return buf_size; + } + + for (tlvh = ri; sum < length; tlvh = TLV_HDR_NEXT(tlvh)) { + switch (ntohs(tlvh->type)) { + case RI_PCE_SUBTLV_ADDRESS: + sum += show_vty_pce_subtlv_address(vty, tlvh); + break; + case RI_PCE_SUBTLV_PATH_SCOPE: + sum += show_vty_pce_subtlv_path_scope(vty, tlvh); + break; + case RI_PCE_SUBTLV_DOMAIN: + sum += show_vty_pce_subtlv_domain(vty, tlvh); + break; + case RI_PCE_SUBTLV_NEIGHBOR: + sum += show_vty_pce_subtlv_neighbor(vty, tlvh); + break; + case RI_PCE_SUBTLV_CAP_FLAG: + sum += show_vty_pce_subtlv_cap_flag(vty, tlvh); + break; + default: + sum += show_vty_unknown_tlv(vty, tlvh, length - sum); + break; + } + } + return sum; +} + +/* Display Segment Routing Algorithm TLV information */ +static uint16_t show_vty_sr_algorithm(struct vty *vty, struct tlv_header *tlvh) +{ + struct ri_sr_tlv_sr_algorithm *algo = + (struct ri_sr_tlv_sr_algorithm *)tlvh; + int i; + + check_tlv_size(ALGORITHM_COUNT, "Segment Routing Algorithm"); + + if (vty != NULL) { + vty_out(vty, " Segment Routing Algorithm TLV:\n"); + for (i = 0; i < ntohs(algo->header.length); i++) { + switch (algo->value[i]) { + case 0: + vty_out(vty, " Algorithm %d: SPF\n", i); + break; + case 1: + vty_out(vty, " Algorithm %d: Strict SPF\n", + i); + break; + default: + vty_out(vty, + " Algorithm %d: Unknown value %d\n", i, + algo->value[i]); + break; + } + } + } else { + zlog_debug(" Segment Routing Algorithm TLV:"); + for (i = 0; i < ntohs(algo->header.length); i++) + switch (algo->value[i]) { + case 0: + zlog_debug(" Algorithm %d: SPF", i); + break; + case 1: + zlog_debug(" Algorithm %d: Strict SPF", i); + break; + default: + zlog_debug(" Algorithm %d: Unknown value %d", + i, algo->value[i]); + break; + } + } + + return TLV_SIZE(tlvh); +} + +/* Display Segment Routing SID/Label Range TLV information */ +static uint16_t show_vty_sr_range(struct vty *vty, struct tlv_header *tlvh) +{ + struct ri_sr_tlv_sid_label_range *range = + (struct ri_sr_tlv_sid_label_range *)tlvh; + + check_tlv_size(RI_SR_TLV_LABEL_RANGE_SIZE, "SR Label Range"); + + if (vty != NULL) { + vty_out(vty, + " Segment Routing %s Range TLV:\n" + " Range Size = %d\n" + " SID Label = %d\n\n", + ntohs(range->header.type) == RI_SR_TLV_SRGB_LABEL_RANGE + ? "Global" + : "Local", + GET_RANGE_SIZE(ntohl(range->size)), + GET_LABEL(ntohl(range->lower.value))); + } else { + zlog_debug( + " Segment Routing %s Range TLV: Range Size = %d SID Label = %d", + ntohs(range->header.type) == RI_SR_TLV_SRGB_LABEL_RANGE + ? "Global" + : "Local", + GET_RANGE_SIZE(ntohl(range->size)), + GET_LABEL(ntohl(range->lower.value))); + } + + return TLV_SIZE(tlvh); +} + +/* Display Segment Routing Maximum Stack Depth TLV information */ +static uint16_t show_vty_sr_msd(struct vty *vty, struct tlv_header *tlvh) +{ + struct ri_sr_tlv_node_msd *msd = (struct ri_sr_tlv_node_msd *)tlvh; + + check_tlv_size(RI_SR_TLV_NODE_MSD_SIZE, "Node Maximum Stack Depth"); + + if (vty != NULL) { + vty_out(vty, + " Segment Routing MSD TLV:\n" + " Node Maximum Stack Depth = %d\n", + msd->value); + } else { + zlog_debug( + " Segment Routing MSD TLV: Node Maximum Stack Depth = %d", + msd->value); + } + + return TLV_SIZE(tlvh); +} + +static void ospf_router_info_show_info(struct vty *vty, + struct json_object *json, + struct ospf_lsa *lsa) +{ + struct lsa_header *lsah = lsa->data; + struct tlv_header *tlvh; + uint16_t length = 0, sum = 0; + + if (json) + return; + + /* Initialize TLV browsing */ + length = lsa->size - OSPF_LSA_HEADER_SIZE; + + for (tlvh = TLV_HDR_TOP(lsah); sum < length && tlvh; + tlvh = TLV_HDR_NEXT(tlvh)) { + switch (ntohs(tlvh->type)) { + case RI_TLV_CAPABILITIES: + sum += show_vty_router_cap(vty, tlvh); + break; + case RI_TLV_PCE: + tlvh++; + sum += TLV_HDR_SIZE; + sum += show_vty_pce_info(vty, tlvh, length - sum); + break; + case RI_SR_TLV_SR_ALGORITHM: + sum += show_vty_sr_algorithm(vty, tlvh); + break; + case RI_SR_TLV_SRGB_LABEL_RANGE: + case RI_SR_TLV_SRLB_LABEL_RANGE: + sum += show_vty_sr_range(vty, tlvh); + break; + case RI_SR_TLV_NODE_MSD: + sum += show_vty_sr_msd(vty, tlvh); + break; + + default: + sum += show_vty_unknown_tlv(vty, tlvh, length); + break; + } + } + + return; +} + +static void ospf_router_info_config_write_router(struct vty *vty) +{ + struct ospf_pce_info *pce = &OspfRI.pce_info; + struct listnode *node; + struct ri_pce_subtlv_domain *domain; + struct ri_pce_subtlv_neighbor *neighbor; + struct in_addr tmp; + + if (!OspfRI.enabled) + return; + + if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) + vty_out(vty, " router-info as\n"); + else + vty_out(vty, " router-info area\n"); + + if (OspfRI.pce_info.enabled) { + + if (pce->pce_address.header.type != 0) + vty_out(vty, " pce address %pI4\n", + &pce->pce_address.address.value); + + if (pce->pce_cap_flag.header.type != 0) + vty_out(vty, " pce flag 0x%x\n", + ntohl(pce->pce_cap_flag.value)); + + for (ALL_LIST_ELEMENTS_RO(pce->pce_domain, node, domain)) { + if (domain->header.type != 0) { + if (domain->type == PCE_DOMAIN_TYPE_AREA) { + tmp.s_addr = domain->value; + vty_out(vty, " pce domain area %pI4\n", + &tmp); + } else { + vty_out(vty, " pce domain as %d\n", + ntohl(domain->value)); + } + } + } + + for (ALL_LIST_ELEMENTS_RO(pce->pce_neighbor, node, neighbor)) { + if (neighbor->header.type != 0) { + if (neighbor->type == PCE_DOMAIN_TYPE_AREA) { + tmp.s_addr = neighbor->value; + vty_out(vty, + " pce neighbor area %pI4\n", + &tmp); + } else { + vty_out(vty, " pce neighbor as %d\n", + ntohl(neighbor->value)); + } + } + } + + if (pce->pce_scope.header.type != 0) + vty_out(vty, " pce scope 0x%x\n", + ntohl(OspfRI.pce_info.pce_scope.value)); + } + return; +} + +/*------------------------------------------------------------------------* + * Following are vty command functions. + *------------------------------------------------------------------------*/ +/* Simple wrapper schedule RI LSA action in function of the scope */ +static void ospf_router_info_schedule(enum lsa_opcode opcode) +{ + struct listnode *node, *nnode; + struct ospf_ri_area_info *ai; + + if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) { + if (CHECK_FLAG(OspfRI.as_flags, RIFLG_LSA_ENGAGED)) + ospf_router_info_lsa_schedule(NULL, opcode); + else if (opcode == REORIGINATE_THIS_LSA) + ospf_router_info_lsa_schedule(NULL, opcode); + } else { + for (ALL_LIST_ELEMENTS(OspfRI.area_info, node, nnode, ai)) { + if (CHECK_FLAG(ai->flags, RIFLG_LSA_ENGAGED)) + ospf_router_info_lsa_schedule(ai, opcode); + } + } +} + +DEFUN (router_info, + router_info_area_cmd, + "router-info ", + OSPF_RI_STR + "Enable the Router Information functionality with AS flooding scope\n" + "Enable the Router Information functionality with Area flooding scope\n" + "OSPF area ID in IP format (deprecated)\n") +{ + int idx_mode = 1; + uint8_t scope; + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + if (OspfRI.enabled) + return CMD_SUCCESS; + + /* Check that the OSPF is using default VRF */ + if (ospf->vrf_id != VRF_DEFAULT) { + vty_out(vty, + "Router Information is only supported in default VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Check and get Area value if present */ + if (strncmp(argv[idx_mode]->arg, "as", 2) == 0) + scope = OSPF_OPAQUE_AS_LSA; + else + scope = OSPF_OPAQUE_AREA_LSA; + + /* First start to register Router Information callbacks */ + if (!OspfRI.registered && (ospf_router_info_register(scope)) != 0) { + vty_out(vty, + "%% Unable to register Router Information callbacks."); + flog_err( + EC_OSPF_INIT_FAIL, + "RI (%s): Unable to register Router Information callbacks. Abort!", + __func__); + return CMD_WARNING_CONFIG_FAILED; + } + + OspfRI.enabled = true; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("RI-> Router Information (%s flooding): OFF -> ON", + OspfRI.scope == OSPF_OPAQUE_AREA_LSA ? "Area" + : "AS"); + + /* + * Following code is intended to handle two cases; + * + * 1) Router Information was disabled at startup time, but now become + * enabled. + * 2) Router Information was once enabled then disabled, and now enabled + * again. + */ + + initialize_params(&OspfRI); + + /* Originate or Refresh RI LSA if already engaged */ + ospf_router_info_schedule(REORIGINATE_THIS_LSA); + return CMD_SUCCESS; +} + + +DEFUN (no_router_info, + no_router_info_cmd, + "no router-info", + NO_STR + "Disable the Router Information functionality\n") +{ + + if (!OspfRI.enabled) + return CMD_SUCCESS; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("RI-> Router Information: ON -> OFF"); + + ospf_router_info_schedule(FLUSH_THIS_LSA); + + OspfRI.enabled = false; + + return CMD_SUCCESS; +} + +static int ospf_ri_enabled(struct vty *vty) +{ + if (OspfRI.enabled) + return 1; + + if (vty) + vty_out(vty, "%% OSPF RI is not turned on\n"); + + return 0; +} + +DEFUN (pce_address, + pce_address_cmd, + "pce address A.B.C.D", + PCE_STR + "Stable IP address of the PCE\n" + "PCE address in IPv4 address format\n") +{ + int idx_ipv4 = 2; + struct in_addr value; + struct ospf_pce_info *pi = &OspfRI.pce_info; + + if (!ospf_ri_enabled(vty)) + return CMD_WARNING_CONFIG_FAILED; + + if (!inet_aton(argv[idx_ipv4]->arg, &value)) { + vty_out(vty, "Please specify PCE Address by A.B.C.D\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (ntohs(pi->pce_address.header.type) == 0 + || ntohl(pi->pce_address.address.value.s_addr) + != ntohl(value.s_addr)) { + + set_pce_address(value, pi); + + /* Refresh RI LSA if already engaged */ + ospf_router_info_schedule(REFRESH_THIS_LSA); + } + + return CMD_SUCCESS; +} + +DEFUN (no_pce_address, + no_pce_address_cmd, + "no pce address [A.B.C.D]", + NO_STR + PCE_STR + "Disable PCE address\n" + "PCE address in IPv4 address format\n") +{ + + unset_param(&OspfRI.pce_info.pce_address); + + /* Refresh RI LSA if already engaged */ + ospf_router_info_schedule(REFRESH_THIS_LSA); + + return CMD_SUCCESS; +} + +DEFUN (pce_path_scope, + pce_path_scope_cmd, + "pce scope BITPATTERN", + PCE_STR + "Path scope visibilities of the PCE for path computation\n" + "32-bit Hexadecimal value\n") +{ + int idx_bitpattern = 2; + uint32_t scope; + struct ospf_pce_info *pi = &OspfRI.pce_info; + + if (!ospf_ri_enabled(vty)) + return CMD_WARNING_CONFIG_FAILED; + + if (sscanf(argv[idx_bitpattern]->arg, "0x%x", &scope) != 1) { + vty_out(vty, "pce_path_scope: fscanf: %s\n", + safe_strerror(errno)); + return CMD_WARNING_CONFIG_FAILED; + } + + if (ntohl(pi->pce_scope.header.type) == 0 + || scope != pi->pce_scope.value) { + set_pce_path_scope(scope, pi); + + /* Refresh RI LSA if already engaged */ + ospf_router_info_schedule(REFRESH_THIS_LSA); + } + + return CMD_SUCCESS; +} + +DEFUN (no_pce_path_scope, + no_pce_path_scope_cmd, + "no pce scope [BITPATTERN]", + NO_STR + PCE_STR + "Disable PCE path scope\n" + "32-bit Hexadecimal value\n") +{ + + unset_param(&OspfRI.pce_info.pce_address); + + /* Refresh RI LSA if already engaged */ + ospf_router_info_schedule(REFRESH_THIS_LSA); + + return CMD_SUCCESS; +} + +DEFUN (pce_domain, + pce_domain_cmd, + "pce domain as (0-65535)", + PCE_STR + "Configure PCE domain AS number\n" + "AS number where the PCE as visibilities for path computation\n" + "AS number in decimal <0-65535>\n") +{ + int idx_number = 3; + + uint32_t as; + struct ospf_pce_info *pce = &OspfRI.pce_info; + struct listnode *node; + struct ri_pce_subtlv_domain *domain; + + if (!ospf_ri_enabled(vty)) + return CMD_WARNING_CONFIG_FAILED; + + if (sscanf(argv[idx_number]->arg, "%" SCNu32, &as) != 1) { + vty_out(vty, "pce_domain: fscanf: %s\n", safe_strerror(errno)); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Check if the domain is not already in the domain list */ + for (ALL_LIST_ELEMENTS_RO(pce->pce_domain, node, domain)) { + if (ntohl(domain->header.type) == 0 && as == domain->value) + return CMD_SUCCESS; + } + + /* Create new domain if not found */ + set_pce_domain(PCE_DOMAIN_TYPE_AS, as, pce); + + /* Refresh RI LSA if already engaged */ + ospf_router_info_schedule(REFRESH_THIS_LSA); + + return CMD_SUCCESS; +} + +DEFUN (no_pce_domain, + no_pce_domain_cmd, + "no pce domain as (0-65535)", + NO_STR + PCE_STR + "Disable PCE domain AS number\n" + "AS number where the PCE as visibilities for path computation\n" + "AS number in decimal <0-65535>\n") +{ + int idx_number = 4; + + uint32_t as; + struct ospf_pce_info *pce = &OspfRI.pce_info; + + if (sscanf(argv[idx_number]->arg, "%" SCNu32, &as) != 1) { + vty_out(vty, "no_pce_domain: fscanf: %s\n", + safe_strerror(errno)); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Unset corresponding PCE domain */ + unset_pce_domain(PCE_DOMAIN_TYPE_AS, as, pce); + + /* Refresh RI LSA if already engaged */ + ospf_router_info_schedule(REFRESH_THIS_LSA); + + return CMD_SUCCESS; +} + +DEFUN (pce_neigbhor, + pce_neighbor_cmd, + "pce neighbor as (0-65535)", + PCE_STR + "Configure PCE neighbor domain AS number\n" + "AS number of PCE neighbors\n" + "AS number in decimal <0-65535>\n") +{ + int idx_number = 3; + + uint32_t as; + struct ospf_pce_info *pce = &OspfRI.pce_info; + struct listnode *node; + struct ri_pce_subtlv_neighbor *neighbor; + + if (!ospf_ri_enabled(vty)) + return CMD_WARNING_CONFIG_FAILED; + + if (sscanf(argv[idx_number]->arg, "%" SCNu32, &as) != 1) { + vty_out(vty, "pce_neighbor: fscanf: %s\n", + safe_strerror(errno)); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Check if the domain is not already in the domain list */ + for (ALL_LIST_ELEMENTS_RO(pce->pce_neighbor, node, neighbor)) { + if (ntohl(neighbor->header.type) == 0 && as == neighbor->value) + return CMD_SUCCESS; + } + + /* Create new domain if not found */ + set_pce_neighbor(PCE_DOMAIN_TYPE_AS, as, pce); + + /* Refresh RI LSA if already engaged */ + ospf_router_info_schedule(REFRESH_THIS_LSA); + + return CMD_SUCCESS; +} + +DEFUN (no_pce_neighbor, + no_pce_neighbor_cmd, + "no pce neighbor as (0-65535)", + NO_STR + PCE_STR + "Disable PCE neighbor AS number\n" + "AS number of PCE neighbor\n" + "AS number in decimal <0-65535>\n") +{ + int idx_number = 4; + + uint32_t as; + struct ospf_pce_info *pce = &OspfRI.pce_info; + + if (sscanf(argv[idx_number]->arg, "%" SCNu32, &as) != 1) { + vty_out(vty, "no_pce_neighbor: fscanf: %s\n", + safe_strerror(errno)); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Unset corresponding PCE domain */ + unset_pce_neighbor(PCE_DOMAIN_TYPE_AS, as, pce); + + /* Refresh RI LSA if already engaged */ + ospf_router_info_schedule(REFRESH_THIS_LSA); + + return CMD_SUCCESS; +} + +DEFUN (pce_cap_flag, + pce_cap_flag_cmd, + "pce flag BITPATTERN", + PCE_STR + "Capabilities of the PCE for path computation\n" + "32-bit Hexadecimal value\n") +{ + int idx_bitpattern = 2; + + uint32_t cap; + struct ospf_pce_info *pce = &OspfRI.pce_info; + + if (!ospf_ri_enabled(vty)) + return CMD_WARNING_CONFIG_FAILED; + + if (sscanf(argv[idx_bitpattern]->arg, "0x%x", &cap) != 1) { + vty_out(vty, "pce_cap_flag: fscanf: %s\n", + safe_strerror(errno)); + return CMD_WARNING_CONFIG_FAILED; + } + + if (ntohl(pce->pce_cap_flag.header.type) == 0 + || cap != pce->pce_cap_flag.value) { + set_pce_cap_flag(cap, pce); + + /* Refresh RI LSA if already engaged */ + ospf_router_info_schedule(REFRESH_THIS_LSA); + } + + return CMD_SUCCESS; +} + +DEFUN (no_pce_cap_flag, + no_pce_cap_flag_cmd, + "no pce flag", + NO_STR + PCE_STR + "Disable PCE capabilities\n") +{ + + unset_param(&OspfRI.pce_info.pce_cap_flag); + + /* Refresh RI LSA if already engaged */ + ospf_router_info_schedule(REFRESH_THIS_LSA); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_router_info, + show_ip_ospf_router_info_cmd, + "show ip ospf router-info", + SHOW_STR + IP_STR + OSPF_STR + "Router Information\n") +{ + + if (OspfRI.enabled) { + vty_out(vty, "--- Router Information parameters ---\n"); + show_vty_router_cap(vty, &OspfRI.router_cap.header); + } else { + if (vty != NULL) + vty_out(vty, + " Router Information is disabled on this router\n"); + } + return CMD_SUCCESS; +} + +DEFUN (show_ip_opsf_router_info_pce, + show_ip_ospf_router_info_pce_cmd, + "show ip ospf router-info pce", + SHOW_STR + IP_STR + OSPF_STR + "Router Information\n" + "PCE information\n") +{ + + struct ospf_pce_info *pce = &OspfRI.pce_info; + struct listnode *node; + struct ri_pce_subtlv_domain *domain; + struct ri_pce_subtlv_neighbor *neighbor; + + if ((OspfRI.enabled) && (OspfRI.pce_info.enabled)) { + vty_out(vty, "--- PCE parameters ---\n"); + + if (pce->pce_address.header.type != 0) + show_vty_pce_subtlv_address(vty, + &pce->pce_address.header); + + if (pce->pce_scope.header.type != 0) + show_vty_pce_subtlv_path_scope(vty, + &pce->pce_scope.header); + + for (ALL_LIST_ELEMENTS_RO(pce->pce_domain, node, domain)) { + if (domain->header.type != 0) + show_vty_pce_subtlv_domain(vty, + &domain->header); + } + + for (ALL_LIST_ELEMENTS_RO(pce->pce_neighbor, node, neighbor)) { + if (neighbor->header.type != 0) + show_vty_pce_subtlv_neighbor(vty, + &neighbor->header); + } + + if (pce->pce_cap_flag.header.type != 0) + show_vty_pce_subtlv_cap_flag(vty, + &pce->pce_cap_flag.header); + + } else { + vty_out(vty, " PCE info is disabled on this router\n"); + } + + return CMD_SUCCESS; +} + +/* Install new CLI commands */ +static void ospf_router_info_register_vty(void) +{ + install_element(VIEW_NODE, &show_ip_ospf_router_info_cmd); + install_element(VIEW_NODE, &show_ip_ospf_router_info_pce_cmd); + + install_element(OSPF_NODE, &router_info_area_cmd); + install_element(OSPF_NODE, &no_router_info_cmd); + install_element(OSPF_NODE, &pce_address_cmd); + install_element(OSPF_NODE, &no_pce_address_cmd); + install_element(OSPF_NODE, &pce_path_scope_cmd); + install_element(OSPF_NODE, &no_pce_path_scope_cmd); + install_element(OSPF_NODE, &pce_domain_cmd); + install_element(OSPF_NODE, &no_pce_domain_cmd); + install_element(OSPF_NODE, &pce_neighbor_cmd); + install_element(OSPF_NODE, &no_pce_neighbor_cmd); + install_element(OSPF_NODE, &pce_cap_flag_cmd); + install_element(OSPF_NODE, &no_pce_cap_flag_cmd); + + return; +} diff --git a/ospfd/ospf_ri.h b/ospfd/ospf_ri.h new file mode 100644 index 0000000..0870938 --- /dev/null +++ b/ospfd/ospf_ri.h @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This is an implementation of RFC4970 Router Information + * with support of RFC5088 PCE Capabilites announcement + * and support of draft-ietf-ospf-segment-routing-extensions-18 + * for Segment Routing Capabilities announcement + * + * + * Module name: Router Information + * Author: Olivier Dugeon + * Copyright (C) 2012 - 2017 Orange Labs http://www.orange.com/ + */ + +#ifndef _ZEBRA_OSPF_ROUTER_INFO_H +#define _ZEBRA_OSPF_ROUTER_INFO_H + +/* + * Opaque LSA's link state ID for Router Information is + * structured as follows. + * + * 24 16 8 0 + * +--------+--------+--------+--------+ + * | 4 | MBZ |........|........| + * +--------+--------+--------+--------+ + * |<-Type->||<-- Instance --->| + * + * + * Type: IANA has assigned '4' for Router Information. + * MBZ: Reserved, must be set to zero. + * Instance: User may select an arbitrary 16-bit value. + * + */ + +/* + * 24 16 8 0 + * +--------+--------+--------+--------+ --- + * | LS age |Options | 9,10,11| A + * +--------+--------+--------+--------+ | + * | 4 | 0 | Instance | | + * +--------+--------+--------+--------+ | + * | Advertising router | | Standard (Opaque) LSA header; + * +--------+--------+--------+--------+ | Type 9,10 or 11 are used. + * | LS sequence number | | + * +--------+--------+--------+--------+ | + * | LS checksum | Length | V + * +--------+--------+--------+--------+ --- + * | Type | Length | A TLV part for Router Information; + * +--------+--------+--------+--------+ | Values might be + * | Values ... | V structured as a set of sub-TLVs. + * +--------+--------+--------+--------+ --- + */ + +/* + * Following section defines TLV body parts. + */ + +/* Up to now, 11 code points have been assigned to Router Information */ +/* Only type 1 Router Capabilities and 6 PCE are supported with this code */ +#define RI_IANA_MAX_TYPE 11 + +/* RFC4970: Router Information Capabilities TLV */ /* Mandatory */ +#define RI_TLV_CAPABILITIES 1 +#define RI_TLV_CAPABILITIES_SIZE 4 +struct ri_tlv_router_cap { + struct tlv_header header; /* Value length is 4 bytes. */ + uint32_t value; +}; + +/* Capabilities bits are left align */ +#define RI_GRACE_RESTART 0x80000000 +#define RI_GRACE_HELPER 0x40000000 +#define RI_STUB_SUPPORT 0x20000000 +#define RI_TE_SUPPORT 0x10000000 +#define RI_P2P_OVER_LAN 0x08000000 +#define RI_TE_EXPERIMENTAL 0x04000000 + +#define RI_TLV_LENGTH 4 + +/* RFC5088: PCE Capabilities TLV */ /* Optional */ +/* RI PCE TLV */ +#define RI_TLV_PCE 6 + +struct ri_tlv_pce { + struct tlv_header header; + /* A set of PCE-sub-TLVs will follow. */ +}; + +/* PCE Address Sub-TLV */ /* Mandatory */ +#define RI_PCE_SUBTLV_ADDRESS 1 +struct ri_pce_subtlv_address { + /* Type = 1; Length is 8 (IPv4) or 20 (IPv6) bytes. */ + struct tlv_header header; +#define PCE_ADDRESS_IPV4_SIZE 8 +#define PCE_ADDRESS_IPV6_SIZE 20 + struct { + uint16_t type; /* Address type: 1 = IPv4, 2 = IPv6 */ +#define PCE_ADDRESS_IPV4 1 +#define PCE_ADDRESS_IPV6 2 + uint16_t reserved; + struct in_addr value; /* PCE address */ + } address; +}; + +/* PCE Path-Scope Sub-TLV */ /* Mandatory */ +#define RI_PCE_SUBTLV_PATH_SCOPE 2 +#define RI_PCE_SUBTLV_PATH_SCOPE_SIZE 4 +struct ri_pce_subtlv_path_scope { + struct tlv_header header; /* Type = 2; Length = 4 bytes. */ + /* + * L, R, Rd, S, Sd, Y, PrefL, PrefR, PrefS and PrefY bits: + * see RFC5088 page 9 + */ + uint32_t value; +}; + +/* PCE Domain Sub-TLV */ /* Optional */ +#define PCE_DOMAIN_TYPE_AREA 1 +#define PCE_DOMAIN_TYPE_AS 2 + +#define RI_PCE_SUBTLV_DOMAIN 3 +#define RI_PCE_SUBTLV_DOMAIN_SIZE 8 +struct ri_pce_subtlv_domain { + struct tlv_header header; /* Type = 3; Length = 8 bytes. */ + uint16_t type; /* Domain type: 1 = OSPF Area ID, 2 = AS Number */ + uint16_t reserved; + uint32_t value; +}; + +/* PCE Neighbor Sub-TLV */ /* Mandatory if R or S bit is set */ +#define RI_PCE_SUBTLV_NEIGHBOR 4 +#define RI_PCE_SUBTLV_NEIGHBOR_SIZE 8 +struct ri_pce_subtlv_neighbor { + struct tlv_header header; /* Type = 4; Length = 8 bytes. */ + uint16_t type; /* Domain type: 1 = OSPF Area ID, 2 = AS Number */ + uint16_t reserved; + uint32_t value; +}; + +/* PCE Capabilities Flags Sub-TLV */ /* Optional */ +#define RI_PCE_SUBTLV_CAP_FLAG 5 +#define RI_PCE_SUBTLV_CAP_FLAG_SIZE 4 + +#define PCE_CAP_GMPLS_LINK 0x0001 +#define PCE_CAP_BIDIRECTIONAL 0x0002 +#define PCE_CAP_DIVERSE_PATH 0x0004 +#define PCE_CAP_LOAD_BALANCE 0x0008 +#define PCE_CAP_SYNCHRONIZED 0x0010 +#define PCE_CAP_OBJECTIVES 0x0020 +#define PCE_CAP_ADDITIVE 0x0040 +#define PCE_CAP_PRIORIZATION 0x0080 +#define PCE_CAP_MULTIPLE_REQ 0x0100 + +struct ri_pce_subtlv_cap_flag { + struct tlv_header header; /* Type = 5; Length = n x 4 bytes. */ + uint32_t value; +}; + +/* Structure to share flooding scope info for Segment Routing */ +struct scope_info { + uint8_t scope; + struct list *areas; +}; + +/* Flags to manage the Router Information LSA. */ +#define RIFLG_LSA_INACTIVE 0x0 +#define RIFLG_LSA_ENGAGED 0x1 +#define RIFLG_LSA_FORCED_REFRESH 0x2 + +/* Store Router Information PCE TLV and SubTLV in network byte order. */ +struct ospf_pce_info { + bool enabled; + struct ri_tlv_pce pce_header; + struct ri_pce_subtlv_address pce_address; + struct ri_pce_subtlv_path_scope pce_scope; + struct list *pce_domain; + struct list *pce_neighbor; + struct ri_pce_subtlv_cap_flag pce_cap_flag; +}; + +/* + * Store Router Information Segment Routing TLV and SubTLV + * in network byte order + */ +struct ospf_ri_sr_info { + bool enabled; + /* Algorithms supported by the node */ + struct ri_sr_tlv_sr_algorithm algo; + /* + * Segment Routing Global Block i.e. label range + * Only one range supported in this code + */ + struct ri_sr_tlv_sid_label_range srgb; + /* + * Segment Routing Local Block. + * Only one block is authorized - see section 3.3 + */ + struct ri_sr_tlv_sid_label_range srlb; + /* Maximum SID Depth supported by the node */ + struct ri_sr_tlv_node_msd msd; +}; + +/* Store area information to flood LSA per area */ +struct ospf_ri_area_info { + + uint32_t flags; + + /* area pointer if flooding is Type 10 Null if flooding is AS scope */ + struct ospf_area *area; +}; + +/* Following structure are internal use only. */ +struct ospf_router_info { + bool enabled; + + uint8_t registered; + uint8_t scope; + /* LSA flags are only used when scope is AS flooding */ + uint32_t as_flags; + + /* List of area info to flood RI LSA */ + struct list *area_info; + + /* Store Router Information Capabilities LSA */ + struct ri_tlv_router_cap router_cap; + + /* Store PCE capability LSA */ + struct ospf_pce_info pce_info; + + /* Store SR capability LSA */ + struct ospf_ri_sr_info sr_info; +}; + +/* Prototypes. */ +extern int ospf_router_info_init(void); +extern void ospf_router_info_term(void); +extern void ospf_router_info_finish(void); +extern int ospf_router_info_enable(void); +extern void ospf_router_info_update_sr(bool enable, struct sr_node *self); +extern struct scope_info ospf_router_info_get_flooding_scope(void); +#endif /* _ZEBRA_OSPF_ROUTER_INFO_H */ diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c new file mode 100644 index 0000000..170909f --- /dev/null +++ b/ospfd/ospf_route.c @@ -0,0 +1,1121 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF routing table. + * Copyright (C) 1999, 2000 Toshiaki Takada + */ + +#include + +#include "prefix.h" +#include "table.h" +#include "memory.h" +#include "linklist.h" +#include "log.h" +#include "if.h" +#include "command.h" +#include "sockunion.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_dump.h" + +const char *ospf_path_type_name(int path_type) +{ + switch (path_type) { + case OSPF_PATH_INTRA_AREA: + return "Intra-Area"; + case OSPF_PATH_INTER_AREA: + return "Inter-Area"; + case OSPF_PATH_TYPE1_EXTERNAL: + return "External-1"; + case OSPF_PATH_TYPE2_EXTERNAL: + return "External-2"; + default: + return "Unknown"; + } +} + +struct ospf_route *ospf_route_new(void) +{ + struct ospf_route *new; + + new = XCALLOC(MTYPE_OSPF_ROUTE, sizeof(struct ospf_route)); + + new->paths = list_new(); + new->paths->del = (void (*)(void *))ospf_path_free; + new->u.std.transit = false; + + return new; +} + +void ospf_route_free(struct ospf_route *or) +{ + if (or->paths) + list_delete(& or->paths); + + XFREE(MTYPE_OSPF_ROUTE, or); +} + +struct ospf_path *ospf_path_new(void) +{ + struct ospf_path *new; + + new = XCALLOC(MTYPE_OSPF_PATH, sizeof(struct ospf_path)); + + return new; +} + +static struct ospf_path *ospf_path_dup(struct ospf_path *path) +{ + struct ospf_path *new; + int memsize; + + new = ospf_path_new(); + memcpy(new, path, sizeof(struct ospf_path)); + + /* optional TI-LFA backup paths */ + if (path->srni.backup_label_stack) { + memsize = sizeof(struct mpls_label_stack) + + (sizeof(mpls_label_t) + * path->srni.backup_label_stack->num_labels); + new->srni.backup_label_stack = + XCALLOC(MTYPE_OSPF_PATH, memsize); + memcpy(new->srni.backup_label_stack, + path->srni.backup_label_stack, memsize); + } + + return new; +} + +void ospf_path_free(struct ospf_path *op) +{ + /* optional TI-LFA backup paths */ + if (op->srni.backup_label_stack) + XFREE(MTYPE_OSPF_PATH, op->srni.backup_label_stack); + + XFREE(MTYPE_OSPF_PATH, op); +} + +void ospf_route_delete(struct ospf *ospf, struct route_table *rt) +{ + struct route_node *rn; + struct ospf_route * or ; + + for (rn = route_top(rt); rn; rn = route_next(rn)) + if ((or = rn->info) != NULL) { + if (or->type == OSPF_DESTINATION_NETWORK) + ospf_zebra_delete( + ospf, (struct prefix_ipv4 *)&rn->p, or); + else if (or->type == OSPF_DESTINATION_DISCARD) + ospf_zebra_delete_discard( + ospf, (struct prefix_ipv4 *)&rn->p); + } +} + +void ospf_route_table_free(struct route_table *rt) +{ + struct route_node *rn; + struct ospf_route * or ; + + for (rn = route_top(rt); rn; rn = route_next(rn)) + if ((or = rn->info) != NULL) { + ospf_route_free(or); + + rn->info = NULL; + route_unlock_node(rn); + } + + route_table_finish(rt); +} + +/* If a prefix exists in the new routing table, then return 1, + otherwise return 0. Since the ZEBRA-RIB does an implicit + withdraw, it is not necessary to send a delete, an add later + will act like an implicit delete. */ +static int ospf_route_exist_new_table(struct route_table *rt, + struct prefix_ipv4 *prefix) +{ + struct route_node *rn; + + assert(rt); + assert(prefix); + + rn = route_node_lookup(rt, (struct prefix *)prefix); + if (!rn) { + return 0; + } + route_unlock_node(rn); + + if (!rn->info) { + return 0; + } + + return 1; +} + +static int ospf_route_backup_path_same(struct sr_nexthop_info *srni1, + struct sr_nexthop_info *srni2) +{ + struct mpls_label_stack *ls1, *ls2; + uint8_t label_count; + + ls1 = srni1->backup_label_stack; + ls2 = srni2->backup_label_stack; + + if (!ls1 && !ls2) + return 1; + + if ((ls1 && !ls2) || (!ls1 && ls2)) + return 0; + + if (ls1->num_labels != ls2->num_labels) + return 0; + + for (label_count = 0; label_count < ls1->num_labels; label_count++) { + if (ls1->label[label_count] != ls2->label[label_count]) + return 0; + } + + if (!IPV4_ADDR_SAME(&srni1->backup_nexthop, &srni2->backup_nexthop)) + return 0; + + return 1; +} + +/* If a prefix and a nexthop match any route in the routing table, + then return 1, otherwise return 0. */ +int ospf_route_match_same(struct route_table *rt, struct prefix_ipv4 *prefix, + struct ospf_route *newor) +{ + struct route_node *rn; + struct ospf_route * or ; + struct ospf_path *op; + struct ospf_path *newop; + struct listnode *n1; + struct listnode *n2; + + if (!rt || !prefix) + return 0; + + rn = route_node_lookup(rt, (struct prefix *)prefix); + if (!rn || !rn->info) + return 0; + + route_unlock_node(rn); + + or = rn->info; + if (or->type == newor->type && or->cost == newor->cost) { + if (or->changed) + return 0; + + if (or->type == OSPF_DESTINATION_NETWORK) { + if (or->paths->count != newor->paths->count) + return 0; + + /* Check each path. */ + for (n1 = listhead(or->paths), + n2 = listhead(newor->paths); + n1 && n2; n1 = listnextnode_unchecked(n1), + n2 = listnextnode_unchecked(n2)) { + op = listgetdata(n1); + newop = listgetdata(n2); + + if (!IPV4_ADDR_SAME(&op->nexthop, + &newop->nexthop)) + return 0; + if (op->ifindex != newop->ifindex) + return 0; + + /* check TI-LFA backup paths */ + if (!ospf_route_backup_path_same(&op->srni, + &newop->srni)) + return 0; + } + return 1; + } else if (prefix_same(&rn->p, (struct prefix *)prefix)) + return 1; + } + return 0; +} + +/* delete routes generated from AS-External routes if there is a inter/intra + * area route + */ +static void ospf_route_delete_same_ext(struct ospf *ospf, + struct route_table *external_routes, + struct route_table *routes) +{ + struct route_node *rn, *ext_rn; + + if ((external_routes == NULL) || (routes == NULL)) + return; + + /* Remove deleted routes */ + for (rn = route_top(routes); rn; rn = route_next(rn)) { + if (rn && rn->info) { + struct prefix_ipv4 *p = (struct prefix_ipv4 *)(&rn->p); + if ((ext_rn = route_node_lookup(external_routes, + (struct prefix *)p))) { + if (ext_rn->info) { + ospf_zebra_delete(ospf, p, + ext_rn->info); + ospf_route_free(ext_rn->info); + ext_rn->info = NULL; + } + route_unlock_node(ext_rn); + } + } + } +} + +/* rt: Old, cmprt: New */ +static void ospf_route_delete_uniq(struct ospf *ospf, struct route_table *rt, + struct route_table *cmprt) +{ + struct route_node *rn; + struct ospf_route * or ; + + for (rn = route_top(rt); rn; rn = route_next(rn)) + if ((or = rn->info) != NULL) + if (or->path_type == OSPF_PATH_INTRA_AREA || + or->path_type == OSPF_PATH_INTER_AREA) { + if (or->type == OSPF_DESTINATION_NETWORK) { + if (!ospf_route_exist_new_table( + cmprt, + (struct prefix_ipv4 *)&rn + ->p)) + ospf_zebra_delete( + ospf, + (struct prefix_ipv4 + *)&rn->p, + or); + } else if (or->type == OSPF_DESTINATION_DISCARD) + if (!ospf_route_exist_new_table( + cmprt, + (struct prefix_ipv4 *)&rn + ->p)) + ospf_zebra_delete_discard( + ospf, + (struct prefix_ipv4 + *)&rn->p); + } +} + +/* Install routes to table. */ +void ospf_route_install(struct ospf *ospf, struct route_table *rt) +{ + struct route_node *rn; + struct ospf_route * or ; + + /* rt contains new routing table, new_table contains an old one. + updating pointers */ + if (ospf->old_table) + ospf_route_table_free(ospf->old_table); + + ospf->old_table = ospf->new_table; + ospf->new_table = rt; + + /* Delete old routes. */ + if (ospf->old_table) + ospf_route_delete_uniq(ospf, ospf->old_table, rt); + if (ospf->old_external_route) + ospf_route_delete_same_ext(ospf, ospf->old_external_route, rt); + + /* Install new routes. */ + for (rn = route_top(rt); rn; rn = route_next(rn)) + if ((or = rn->info) != NULL) { + if (or->type == OSPF_DESTINATION_NETWORK) { + if (!ospf_route_match_same( + ospf->old_table, + (struct prefix_ipv4 *)&rn->p, or)) + ospf_zebra_add( + ospf, + (struct prefix_ipv4 *)&rn->p, + or); + } else if (or->type == OSPF_DESTINATION_DISCARD) + if (!ospf_route_match_same( + ospf->old_table, + (struct prefix_ipv4 *)&rn->p, or)) + ospf_zebra_add_discard( + ospf, + (struct prefix_ipv4 *)&rn->p); + } +} + +/* RFC2328 16.1. (4). For "router". */ +void ospf_intra_add_router(struct route_table *rt, struct vertex *v, + struct ospf_area *area, bool add_only) +{ + struct route_node *rn; + struct ospf_route * or ; + struct prefix_ipv4 p; + struct router_lsa *lsa; + + if (IS_DEBUG_OSPF_EVENT) { + if (!add_only) + zlog_debug("%s: Start", __func__); + else + zlog_debug("%s: REACHRUN: Start", __func__); + } + lsa = (struct router_lsa *)v->lsa; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: LS ID: %pI4", __func__, &lsa->header.id); + + if (!add_only) { + if (!OSPF_IS_AREA_BACKBONE(area)) + ospf_vl_up_check(area, lsa->header.id, v); + + if (!CHECK_FLAG(lsa->flags, ROUTER_LSA_SHORTCUT)) + area->shortcut_capability = 0; + + /* If the newly added vertex is an area border router or AS + boundary router, a routing table entry is added whose + destination type is "router". */ + if (!IS_ROUTER_LSA_BORDER(lsa) && + !IS_ROUTER_LSA_EXTERNAL(lsa)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: this router is neither ASBR nor ABR, skipping it", + __func__); + return; + } + + /* Update ABR and ASBR count in this area. */ + if (IS_ROUTER_LSA_BORDER(lsa)) + area->abr_count++; + if (IS_ROUTER_LSA_EXTERNAL(lsa)) + area->asbr_count++; + } + + /* The Options field found in the associated router-LSA is copied + into the routing table entry's Optional capabilities field. Call + the newly added vertex Router X. */ + or = ospf_route_new(); + + or->id = v->id; + or->u.std.area_id = area->area_id; + or->u.std.external_routing = area->external_routing; + or->path_type = OSPF_PATH_INTRA_AREA; + or->cost = v->distance; + or->type = OSPF_DESTINATION_ROUTER; + or->u.std.origin = (struct lsa_header *)lsa; + or->u.std.options = lsa->header.options; + or->u.std.flags = lsa->flags; + + /* If Router X is the endpoint of one of the calculating router's + virtual links, and the virtual link uses Area A as Transit area: + the virtual link is declared up, the IP address of the virtual + interface is set to the IP address of the outgoing interface + calculated above for Router X, and the virtual neighbor's IP + address is set to Router X's interface address (contained in + Router X's router-LSA) that points back to the root of the + shortest- path tree; equivalently, this is the interface that + points back to Router X's parent vertex on the shortest-path tree + (similar to the calculation in Section 16.1.1). */ + + p.family = AF_INET; + p.prefix = v->id; + p.prefixlen = IPV4_MAX_BITLEN; + apply_mask_ipv4(&p); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: talking about %pFX", __func__, &p); + + rn = route_node_get(rt, (struct prefix *)&p); + + /* Note that we keep all routes to ABRs and ASBRs, not only the best */ + if (rn->info == NULL) + rn->info = list_new(); + else + route_unlock_node(rn); + + ospf_route_copy_nexthops_from_vertex(area, or, v); + + listnode_add(rn->info, or); + + if (IS_DEBUG_OSPF_EVENT) { + if (!add_only) + zlog_debug("%s: Stop", __func__); + else + zlog_debug("%s: REACHRUN: Stop", __func__); + } +} + +/* RFC2328 16.1. (4). For transit network. */ +void ospf_intra_add_transit(struct route_table *rt, struct vertex *v, + struct ospf_area *area) +{ + struct route_node *rn; + struct ospf_route * or ; + struct prefix_ipv4 p; + struct network_lsa *lsa; + + lsa = (struct network_lsa *)v->lsa; + + /* If the newly added vertex is a transit network, the routing table + entry for the network is located. The entry's Destination ID is + the IP network number, which can be obtained by masking the + Vertex ID (Link State ID) with its associated subnet mask (found + in the body of the associated network-LSA). */ + if (lsa->mask.s_addr == 0xffffffff) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Suppress installing LSA[Type2,%pI4] route due to host mask", + &(lsa->header.id)); + return; + } + p.family = AF_INET; + p.prefix = v->id; + p.prefixlen = ip_masklen(lsa->mask); + apply_mask_ipv4(&p); + + rn = route_node_get(rt, (struct prefix *)&p); + + /* If the routing table entry already exists (i.e., there is already + an intra-area route to the destination installed in the routing + table), multiple vertices have mapped to the same IP network. + For example, this can occur when a new Designated Router is being + established. In this case, the current routing table entry + should be overwritten if and only if the newly found path is just + as short and the current routing table entry's Link State Origin + has a smaller Link State ID than the newly added vertex' LSA. */ + if (rn->info) { + struct ospf_route *cur_or; + + route_unlock_node(rn); + cur_or = rn->info; + + if (v->distance > cur_or->cost + || IPV4_ADDR_CMP(&cur_or->u.std.origin->id, &lsa->header.id) + > 0) + return; + + ospf_route_free(rn->info); + } + + or = ospf_route_new(); + + or->id = v->id; + or->u.std.area_id = area->area_id; + or->u.std.external_routing = area->external_routing; + or->path_type = OSPF_PATH_INTRA_AREA; + or->cost = v->distance; + or->type = OSPF_DESTINATION_NETWORK; + or->u.std.origin = (struct lsa_header *)lsa; + or->u.std.transit = true; + + ospf_route_copy_nexthops_from_vertex(area, or, v); + + rn->info = or ; +} + +/* RFC2328 16.1. second stage. */ +void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, + struct vertex *v, struct ospf_area *area, + int parent_is_root, int lsa_pos) +{ + uint32_t cost; + struct route_node *rn; + struct ospf_route * or ; + struct prefix_ipv4 p; + struct router_lsa *lsa; + struct ospf_interface *oi = NULL; + struct ospf_path *path; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Start", __func__); + + lsa = (struct router_lsa *)v->lsa; + + p.family = AF_INET; + p.prefix = link->link_id; + p.prefixlen = ip_masklen(link->link_data); + apply_mask_ipv4(&p); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: processing route to %pFX", __func__, &p); + + /* (1) Calculate the distance D of stub network from the root. D is + equal to the distance from the root to the router vertex + (calculated in stage 1), plus the stub network link's advertised + cost. */ + cost = v->distance + ntohs(link->m[0].metric); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: calculated cost is %d + %d = %d", __func__, + v->distance, ntohs(link->m[0].metric), cost); + + /* PtP links with /32 masks adds host routes to remote, directly + * connected hosts, see RFC 2328, 12.4.1.1, Option 1. + * Such routes can just be ignored for the sake of tidyness. + */ + if (parent_is_root && link->link_data.s_addr == 0xffffffff + && ospf_if_lookup_by_local_addr(area->ospf, NULL, link->link_id)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: ignoring host route %pI4/32 to self.", + __func__, &link->link_id); + return; + } + + rn = route_node_get(rt, (struct prefix *)&p); + + /* Lookup current routing table. */ + if (rn->info) { + struct ospf_route *cur_or; + + route_unlock_node(rn); + + cur_or = rn->info; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: another route to the same prefix found with cost %u", + __func__, cur_or->cost); + + /* Compare this distance to the current best cost to the stub + network. This is done by looking up the stub network's + current routing table entry. If the calculated distance D is + larger, go on to examine the next stub network link in the + LSA. */ + if (cost > cur_or->cost) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: old route is better, exit", + __func__); + return; + } + + /* (2) If this step is reached, the stub network's routing table + entry must be updated. Calculate the set of next hops that + would result from using the stub network link. This + calculation is shown in Section 16.1.1; input to this + calculation is the destination (the stub network) and the + parent vertex (the router vertex). If the distance D is the + same as the current routing table cost, simply add this set + of next hops to the routing table entry's list of next hops. + In this case, the routing table already has a Link State + Origin. If this Link State Origin is a router-LSA whose Link + State ID is smaller than V's Router ID, reset the Link State + Origin to V's router-LSA. */ + + if (cost == cur_or->cost) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: routes are equal, merge", + __func__); + + ospf_route_copy_nexthops_from_vertex(area, cur_or, v); + + if (IPV4_ADDR_CMP(&cur_or->u.std.origin->id, + &lsa->header.id) + < 0) + cur_or->u.std.origin = (struct lsa_header *)lsa; + return; + } + + /* Otherwise D is smaller than the routing table cost. + Overwrite the current routing table entry by setting the + routing table entry's cost to D, and by setting the entry's + list of next hops to the newly calculated set. Set the + routing table entry's Link State Origin to V's router-LSA. + Then go on to examine the next stub network link. */ + + if (cost < cur_or->cost) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: new route is better, set it", + __func__); + + cur_or->cost = cost; + + list_delete_all_node(cur_or->paths); + + ospf_route_copy_nexthops_from_vertex(area, cur_or, v); + + cur_or->u.std.origin = (struct lsa_header *)lsa; + return; + } + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: installing new route", __func__); + + or = ospf_route_new(); + + or->id = v->id; + or->u.std.area_id = area->area_id; + or->u.std.external_routing = area->external_routing; + or->path_type = OSPF_PATH_INTRA_AREA; + or->cost = cost; + or->type = OSPF_DESTINATION_NETWORK; + or->u.std.origin = (struct lsa_header *)lsa; + + /* Nexthop is depend on connection type. */ + if (v != area->spf) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: this network is on remote router", + __func__); + ospf_route_copy_nexthops_from_vertex(area, or, v); + } else { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: this network is on this router", + __func__); + + /* + * Only deal with interface data when we + * don't do a dry run + */ + if (!area->spf_dry_run) + oi = ospf_if_lookup_by_lsa_pos(area, lsa_pos); + + if (oi || area->spf_dry_run) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: the lsa pos is %d", __func__, + lsa_pos); + + path = ospf_path_new(); + path->nexthop.s_addr = INADDR_ANY; + + if (oi) { + path->ifindex = oi->ifp->ifindex; + if (CHECK_FLAG(oi->connected->flags, + ZEBRA_IFA_UNNUMBERED)) + path->unnumbered = 1; + } + + listnode_add(or->paths, path); + } else { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: where's the interface ?", + __func__); + } + } + if (rn->info) + ospf_route_free(rn->info); + + rn->info = or ; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Stop", __func__); +} + +static const char *const ospf_path_type_str[] = { + "unknown-type", "intra-area", "inter-area", "type1-external", + "type2-external" +}; + +void ospf_route_table_dump(struct route_table *rt) +{ + struct route_node *rn; + struct ospf_route * or ; + struct listnode *pnode; + struct ospf_path *path; + + zlog_debug("========== OSPF routing table =========="); + for (rn = route_top(rt); rn; rn = route_next(rn)) + if ((or = rn->info) != NULL) { + if (or->type == OSPF_DESTINATION_NETWORK) { + zlog_debug("N %-18pFX %-15pI4 %s %d", &rn->p, + &or->u.std.area_id, + ospf_path_type_str[or->path_type], + or->cost); + for (ALL_LIST_ELEMENTS_RO(or->paths, pnode, + path)) + zlog_debug(" -> %pI4", + &path->nexthop); + } else + zlog_debug("R %-18pI4 %-15pI4 %s %d", + &rn->p.u.prefix4, + &or->u.std.area_id, + ospf_path_type_str[or->path_type], + or->cost); + } + zlog_debug("========================================"); +} + +void ospf_router_route_table_dump(struct route_table *rt) +{ + struct route_node *rn; + struct ospf_route *or; + struct listnode *node; + + zlog_debug("========== OSPF routing table =========="); + for (rn = route_top(rt); rn; rn = route_next(rn)) { + for (ALL_LIST_ELEMENTS_RO((struct list *)rn->info, node, or)) { + assert(or->type == OSPF_DESTINATION_ROUTER); + zlog_debug("R %-18pI4 %-15pI4 %s %d", &rn->p.u.prefix4, + &or->u.std.area_id, + ospf_path_type_str[or->path_type], or->cost); + } + } + zlog_debug("========================================"); +} + +/* This is 16.4.1 implementation. + o Intra-area paths using non-backbone areas are always the most preferred. + o The other paths, intra-area backbone paths and inter-area paths, + are of equal preference. */ +static int ospf_asbr_route_cmp(struct ospf *ospf, struct ospf_route *r1, + struct ospf_route *r2) +{ + uint8_t r1_type, r2_type; + + r1_type = r1->path_type; + r2_type = r2->path_type; + + /* r1/r2 itself is backbone, and it's Inter-area path. */ + if (OSPF_IS_AREA_ID_BACKBONE(r1->u.std.area_id)) + r1_type = OSPF_PATH_INTER_AREA; + if (OSPF_IS_AREA_ID_BACKBONE(r2->u.std.area_id)) + r2_type = OSPF_PATH_INTER_AREA; + + return (r1_type - r2_type); +} + +/* Compare two routes. + ret < 0 -- r1 is better. + ret == 0 -- r1 and r2 are the same. + ret > 0 -- r2 is better. */ +int ospf_route_cmp(struct ospf *ospf, struct ospf_route *r1, + struct ospf_route *r2) +{ + int ret = 0; + + /* Path types of r1 and r2 are not the same. */ + if ((ret = (r1->path_type - r2->path_type))) + return ret; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Route[Compare]: Path types are the same."); + /* Path types are the same, compare any cost. */ + switch (r1->path_type) { + case OSPF_PATH_INTRA_AREA: + case OSPF_PATH_INTER_AREA: + break; + case OSPF_PATH_TYPE1_EXTERNAL: + if (!CHECK_FLAG(ospf->config, OSPF_RFC1583_COMPATIBLE)) { + ret = ospf_asbr_route_cmp(ospf, r1->u.ext.asbr, + r2->u.ext.asbr); + if (ret != 0) + return ret; + } + break; + case OSPF_PATH_TYPE2_EXTERNAL: + if ((ret = (r1->u.ext.type2_cost - r2->u.ext.type2_cost))) + return ret; + + if (!CHECK_FLAG(ospf->config, OSPF_RFC1583_COMPATIBLE)) { + ret = ospf_asbr_route_cmp(ospf, r1->u.ext.asbr, + r2->u.ext.asbr); + if (ret != 0) + return ret; + } + break; + } + + /* Anyway, compare the costs. */ + return (r1->cost - r2->cost); +} + +static int ospf_path_exist(struct list *plist, struct in_addr nexthop, + struct ospf_interface *oi) +{ + struct listnode *node, *nnode; + struct ospf_path *path; + + for (ALL_LIST_ELEMENTS(plist, node, nnode, path)) + if (IPV4_ADDR_SAME(&path->nexthop, &nexthop) + && path->ifindex == oi->ifp->ifindex) + return 1; + + return 0; +} + +void ospf_route_copy_nexthops_from_vertex(struct ospf_area *area, + struct ospf_route *to, + struct vertex *v) +{ + struct listnode *node; + struct ospf_path *path; + struct vertex_nexthop *nexthop; + struct vertex_parent *vp; + struct ospf_interface *oi = NULL; + + assert(to->paths); + + for (ALL_LIST_ELEMENTS_RO(v->parents, node, vp)) { + nexthop = vp->nexthop; + + /* + * Only deal with interface data when we + * don't do a dry run + */ + if (!area->spf_dry_run) + oi = ospf_if_lookup_by_lsa_pos(area, nexthop->lsa_pos); + + if ((oi && !ospf_path_exist(to->paths, nexthop->router, oi)) + || area->spf_dry_run) { + path = ospf_path_new(); + path->nexthop = nexthop->router; + path->adv_router = v->lsa->adv_router; + + if (oi) { + path->ifindex = oi->ifp->ifindex; + if (CHECK_FLAG(oi->connected->flags, + ZEBRA_IFA_UNNUMBERED)) + path->unnumbered = 1; + } + + listnode_add(to->paths, path); + } + } +} + +struct ospf_path *ospf_path_lookup(struct list *plist, struct ospf_path *path) +{ + struct listnode *node; + struct ospf_path *op; + + for (ALL_LIST_ELEMENTS_RO(plist, node, op)) { + if (!IPV4_ADDR_SAME(&op->nexthop, &path->nexthop)) + continue; + if (!IPV4_ADDR_SAME(&op->adv_router, &path->adv_router)) + continue; + if (op->ifindex != path->ifindex) + continue; + return op; + } + return NULL; +} + +void ospf_route_copy_nexthops(struct ospf_route *to, struct list *from) +{ + struct listnode *node, *nnode; + struct ospf_path *path; + + assert(to->paths); + + for (ALL_LIST_ELEMENTS(from, node, nnode, path)) + /* The same routes are just discarded. */ + if (!ospf_path_lookup(to->paths, path)) + listnode_add(to->paths, ospf_path_dup(path)); +} + +void ospf_route_subst_nexthops(struct ospf_route *to, struct list *from) +{ + + list_delete_all_node(to->paths); + ospf_route_copy_nexthops(to, from); +} + +void ospf_route_subst(struct route_node *rn, struct ospf_route *new_or, + struct ospf_route *over) +{ + route_lock_node(rn); + ospf_route_free(rn->info); + + ospf_route_copy_nexthops(new_or, over->paths); + rn->info = new_or; + route_unlock_node(rn); +} + +void ospf_route_add(struct route_table *rt, struct prefix_ipv4 *p, + struct ospf_route *new_or, struct ospf_route *over) +{ + struct route_node *rn; + + rn = route_node_get(rt, (struct prefix *)p); + + ospf_route_copy_nexthops(new_or, over->paths); + + if (rn->info) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: something's wrong !", __func__); + route_unlock_node(rn); + return; + } + + rn->info = new_or; +} + +void ospf_prune_unreachable_networks(struct route_table *rt) +{ + struct route_node *rn, *next; + struct ospf_route * or ; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Pruning unreachable networks"); + + for (rn = route_top(rt); rn; rn = next) { + next = route_next(rn); + if (rn->info != NULL) { + or = rn->info; + if (listcount(or->paths) == 0) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Pruning route to %pFX", + &rn->p); + + ospf_route_free(or); + rn->info = NULL; + route_unlock_node(rn); + } + } + } +} + +void ospf_prune_unreachable_routers(struct route_table *rtrs) +{ + struct route_node *rn, *next; + struct ospf_route * or ; + struct listnode *node, *nnode; + struct list *paths; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Pruning unreachable routers"); + + for (rn = route_top(rtrs); rn; rn = next) { + next = route_next(rn); + if ((paths = rn->info) == NULL) + continue; + + for (ALL_LIST_ELEMENTS(paths, node, nnode, or)) { + if (listcount(or->paths) == 0) { + if (IS_DEBUG_OSPF_EVENT) { + zlog_debug("Pruning route to rtr %pI4", + &rn->p.u.prefix4); + zlog_debug( + " via area %pI4", + &or->u.std.area_id); + } + + /* Unset the DNA flag on lsa, if the router + * which generated this lsa is no longer + * reachabele. + */ + (CHECK_FLAG(or->u.std.origin->ls_age, + DO_NOT_AGE)) + ? UNSET_FLAG(or->u.std.origin->ls_age, + DO_NOT_AGE) + : 0; + + listnode_delete(paths, or); + ospf_route_free(or); + } + } + + if (listcount(paths) == 0) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Pruning router node %pI4", + &rn->p.u.prefix4); + + list_delete(&paths); + rn->info = NULL; + route_unlock_node(rn); + } + } +} + +int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt, + struct ospf_area *area, struct prefix_ipv4 *p, + bool nssa) +{ + struct route_node *rn; + struct ospf_route * or, *new_or; + + rn = route_node_get(rt, (struct prefix *)p); + + if (rn == NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: router installation error", __func__); + return 0; + } + + if (rn->info) /* If the route to the same destination is found */ + { + route_unlock_node(rn); + + or = rn->info; + + if (!nssa && or->path_type == OSPF_PATH_INTRA_AREA) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: an intra-area route exists", + __func__); + return 0; + } + + if (or->type == OSPF_DESTINATION_DISCARD) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: discard entry already installed", + __func__); + return 0; + } + + ospf_route_free(rn->info); + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: adding %pFX", __func__, p); + + new_or = ospf_route_new(); + new_or->type = OSPF_DESTINATION_DISCARD; + new_or->id.s_addr = INADDR_ANY; + new_or->cost = 0; + new_or->u.std.area_id = area->area_id; + new_or->u.std.external_routing = area->external_routing; + if (nssa) + new_or->path_type = OSPF_PATH_TYPE2_EXTERNAL; + else + new_or->path_type = OSPF_PATH_INTER_AREA; + rn->info = new_or; + + ospf_zebra_add_discard(ospf, p); + + return 1; +} + +void ospf_delete_discard_route(struct ospf *ospf, struct route_table *rt, + struct prefix_ipv4 *p, bool nssa) +{ + struct route_node *rn; + struct ospf_route * or ; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: deleting %pFX", __func__, p); + + rn = route_node_lookup(rt, (struct prefix *)p); + + if (rn == NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: no route found", __func__); + return; + } + + or = rn->info; + + if (!nssa && or->path_type == OSPF_PATH_INTRA_AREA) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: an intra-area route exists", __func__); + return; + } + + if (or->type != OSPF_DESTINATION_DISCARD) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: not a discard entry", __func__); + return; + } + + /* free the route entry and the route node */ + ospf_route_free(rn->info); + + rn->info = NULL; + route_unlock_node(rn); + route_unlock_node(rn); + + /* remove the discard entry from the rib */ + ospf_zebra_delete_discard(ospf, p); + + return; +} diff --git a/ospfd/ospf_route.h b/ospfd/ospf_route.h new file mode 100644 index 0000000..44e8021 --- /dev/null +++ b/ospfd/ospf_route.h @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF routing table. + * Copyright (C) 1999, 2000 Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPF_ROUTE_H +#define _ZEBRA_OSPF_ROUTE_H + +#define OSPF_DESTINATION_ROUTER 1 +#define OSPF_DESTINATION_NETWORK 2 +#define OSPF_DESTINATION_DISCARD 3 + +#define OSPF_PATH_MIN 0 +#define OSPF_PATH_INTRA_AREA 1 +#define OSPF_PATH_INTER_AREA 2 +#define OSPF_PATH_TYPE1_EXTERNAL 3 +#define OSPF_PATH_TYPE2_EXTERNAL 4 +#define OSPF_PATH_MAX 5 + +/* Segment Routing information to complement ospf_path structure */ +struct sr_nexthop_info { + /* Output label associated to this route */ + mpls_label_t label_out; + /* + * Pointer to SR Node which is the next hop for this route + * or NULL if next hop is the destination of the prefix + */ + struct sr_node *nexthop; + + /* TI-LFA */ + struct mpls_label_stack *backup_label_stack; + struct in_addr backup_nexthop; +}; + +/* OSPF Path. */ +struct ospf_path { + struct in_addr nexthop; + struct in_addr adv_router; + ifindex_t ifindex; + unsigned char unnumbered; + struct sr_nexthop_info srni; +}; + +/* Below is the structure linked to every + route node. Note that for Network routing + entries a single ospf_route is kept, while + for ABRs and ASBRs (Router routing entries), + we link an instance of ospf_router_route + where a list of paths is maintained, so + + nr->info is a (struct ospf_route *) for OSPF_DESTINATION_NETWORK + but + nr->info is a (struct ospf_router_route *) for OSPF_DESTINATION_ROUTER +*/ + +struct route_standard { + /* Link Sate Origin. */ + struct lsa_header *origin; + + /* Associated Area. */ + struct in_addr area_id; /* The area the route belongs to */ + + /* Area Type */ + int external_routing; + + /* Optional Capability. */ + uint8_t options; /* Get from LSA header. */ + + /* */ + uint8_t flags; /* From router-LSA */ + + bool transit; /* Transit network or not */ +}; + +struct route_external { + /* Link State Origin. */ + struct ospf_lsa *origin; + + /* Link State Cost Type2. */ + uint32_t type2_cost; + + /* Tag value. */ + uint32_t tag; + + /* ASBR route. */ + struct ospf_route *asbr; +}; + +struct ospf_route { + /* Destination Type. */ + uint8_t type; + + /* Destination ID. */ /* i.e. Link State ID. */ + struct in_addr id; + + /* Address Mask. */ + struct in_addr mask; /* Only valid for networks. */ + + /* Path Type. */ + uint8_t path_type; + + /* List of Paths. */ + struct list *paths; + + /* Link State Cost. */ + uint32_t cost; /* i.e. metric. */ + + /* Route specific info. */ + union { + struct route_standard std; + struct route_external ext; + } u; + + bool changed; +}; + +extern const char *ospf_path_type_name(int path_type); +extern struct ospf_path *ospf_path_new(void); +extern void ospf_path_free(struct ospf_path *); +extern struct ospf_path *ospf_path_lookup(struct list *, struct ospf_path *); +extern struct ospf_route *ospf_route_new(void); +extern void ospf_route_free(struct ospf_route *); +extern void ospf_route_delete(struct ospf *, struct route_table *); +extern void ospf_route_table_free(struct route_table *); + +extern void ospf_route_install(struct ospf *, struct route_table *); +extern void ospf_route_table_dump(struct route_table *); +extern void ospf_router_route_table_dump(struct route_table *rt); + +extern void ospf_intra_add_router(struct route_table *rt, struct vertex *v, + struct ospf_area *area, bool add_all); + +extern void ospf_intra_add_transit(struct route_table *, struct vertex *, + struct ospf_area *); + +extern void ospf_intra_add_stub(struct route_table *, struct router_lsa_link *, + struct vertex *, struct ospf_area *, + int parent_is_root, int); + +extern int ospf_route_cmp(struct ospf *, struct ospf_route *, + struct ospf_route *); +extern void ospf_route_copy_nexthops(struct ospf_route *, struct list *); +extern void ospf_route_copy_nexthops_from_vertex(struct ospf_area *area, + struct ospf_route *, + struct vertex *); + +extern void ospf_route_subst(struct route_node *, struct ospf_route *, + struct ospf_route *); +extern void ospf_route_add(struct route_table *, struct prefix_ipv4 *, + struct ospf_route *, struct ospf_route *); + +extern void ospf_route_subst_nexthops(struct ospf_route *, struct list *); +extern void ospf_prune_unreachable_networks(struct route_table *); +extern void ospf_prune_unreachable_routers(struct route_table *); +extern int ospf_add_discard_route(struct ospf *, struct route_table *, + struct ospf_area *, struct prefix_ipv4 *, + bool); +extern void ospf_delete_discard_route(struct ospf *, struct route_table *, + struct prefix_ipv4 *, bool); +extern int ospf_route_match_same(struct route_table *, struct prefix_ipv4 *, + struct ospf_route *); + +#endif /* _ZEBRA_OSPF_ROUTE_H */ diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c new file mode 100644 index 0000000..d2f6390 --- /dev/null +++ b/ospfd/ospf_routemap.c @@ -0,0 +1,745 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Route map function of ospfd. + * Copyright (C) 2000 IP Infusion Inc. + * + * Written by Toshiaki Takada. + */ + +#include + +#include "memory.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "routemap.h" +#include "command.h" +#include "log.h" +#include "plist.h" +#include "vrf.h" +#include "frrstr.h" +#include "northbound_cli.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_errors.h" + +/* Hook function for updating route_map assignment. */ +static void ospf_route_map_update(const char *name) +{ + struct ospf *ospf; + int type; + struct listnode *n1 = NULL; + + /* If OSPF instatnce does not exist, return right now. */ + if (listcount(om->ospf) == 0) + return; + + for (ALL_LIST_ELEMENTS_RO(om->ospf, n1, ospf)) { + /* Update route-map */ + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { + struct list *red_list; + struct listnode *node; + struct ospf_redist *red; + + red_list = ospf->redist[type]; + if (!red_list) + continue; + + for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) { + if (ROUTEMAP_NAME(red) + && strcmp(ROUTEMAP_NAME(red), name) == 0) { + /* Keep old route-map. */ + struct route_map *old = ROUTEMAP(red); + + ROUTEMAP(red) = + route_map_lookup_by_name( + ROUTEMAP_NAME(red)); + + if (!old) + route_map_counter_increment( + ROUTEMAP(red)); + + /* No update for this distribute type. + */ + if (old == NULL + && ROUTEMAP(red) == NULL) + continue; + + ospf_distribute_list_update( + ospf, type, red->instance); + } + } + } + } +} + +static void ospf_route_map_event(const char *name) +{ + struct ospf *ospf; + int type; + struct listnode *n1 = NULL; + + for (ALL_LIST_ELEMENTS_RO(om->ospf, n1, ospf)) { + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { + struct list *red_list; + struct listnode *node; + struct ospf_redist *red; + + red_list = ospf->redist[type]; + if (!red_list) + continue; + + for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) { + if (ROUTEMAP_NAME(red) && ROUTEMAP(red) + && !strcmp(ROUTEMAP_NAME(red), name)) { + ospf_distribute_list_update( + ospf, type, red->instance); + } + } + } + } +} + +/* `match ip netxthop ' */ +/* Match function return 1 if match is success else return zero. */ +static enum route_map_cmd_result_t +route_match_ip_nexthop(void *rule, const struct prefix *prefix, void *object) +{ + struct access_list *alist; + struct external_info *ei = object; + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefix = ei->nexthop; + p.prefixlen = IPV4_MAX_BITLEN; + + alist = access_list_lookup(AFI_IP, (char *)rule); + if (alist == NULL) { + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) + zlog_debug( + "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", + __func__, (char *)rule); + return RMAP_NOMATCH; + } + + return (access_list_apply(alist, &p) == FILTER_DENY ? RMAP_NOMATCH + : RMAP_MATCH); +} + +/* Route map `ip next-hop' match statement. `arg' should be + access-list name. */ +static void *route_match_ip_nexthop_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `ip address' value. */ +static void route_match_ip_nexthop_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for metric matching. */ +static const struct route_map_rule_cmd route_match_ip_nexthop_cmd = { + "ip next-hop", + route_match_ip_nexthop, + route_match_ip_nexthop_compile, + route_match_ip_nexthop_free +}; + +/* `match ip next-hop prefix-list PREFIX_LIST' */ + +static enum route_map_cmd_result_t +route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, + void *object) +{ + struct prefix_list *plist; + struct external_info *ei = object; + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefix = ei->nexthop; + p.prefixlen = IPV4_MAX_BITLEN; + + plist = prefix_list_lookup(AFI_IP, (char *)rule); + if (plist == NULL) { + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) + zlog_debug( + "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", + __func__, (char *)rule); + return RMAP_NOMATCH; + } + + return (prefix_list_apply(plist, &p) == PREFIX_DENY ? RMAP_NOMATCH + : RMAP_MATCH); +} + +static void *route_match_ip_next_hop_prefix_list_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void route_match_ip_next_hop_prefix_list_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static const struct route_map_rule_cmd + route_match_ip_next_hop_prefix_list_cmd = { + "ip next-hop prefix-list", + route_match_ip_next_hop_prefix_list, + route_match_ip_next_hop_prefix_list_compile, + route_match_ip_next_hop_prefix_list_free +}; + +/* `match ip next-hop type ' */ + +static enum route_map_cmd_result_t +route_match_ip_next_hop_type(void *rule, const struct prefix *prefix, + void *object) +{ + struct external_info *ei = object; + + if (prefix->family == AF_INET) { + ei = (struct external_info *)object; + if (!ei) + return RMAP_NOMATCH; + + if (ei->nexthop.s_addr == INADDR_ANY && !ei->ifindex) + return RMAP_MATCH; + } + return RMAP_NOMATCH; +} + +static void *route_match_ip_next_hop_type_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void route_match_ip_next_hop_type_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static const struct route_map_rule_cmd + route_match_ip_next_hop_type_cmd = { + "ip next-hop type", + route_match_ip_next_hop_type, + route_match_ip_next_hop_type_compile, + route_match_ip_next_hop_type_free +}; + +/* `match ip address IP_ACCESS_LIST' */ +/* Match function should return 1 if match is success else return + zero. */ +static enum route_map_cmd_result_t +route_match_ip_address(void *rule, const struct prefix *prefix, void *object) +{ + struct access_list *alist; + /* struct prefix_ipv4 match; */ + + alist = access_list_lookup(AFI_IP, (char *)rule); + if (alist == NULL) { + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) + zlog_debug( + "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", + __func__, (char *)rule); + return RMAP_NOMATCH; + } + + return (access_list_apply(alist, prefix) == FILTER_DENY ? RMAP_NOMATCH + : RMAP_MATCH); +} + +/* Route map `ip address' match statement. `arg' should be + access-list name. */ +static void *route_match_ip_address_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `ip address' value. */ +static void route_match_ip_address_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ip address matching. */ +static const struct route_map_rule_cmd route_match_ip_address_cmd = { + "ip address", + route_match_ip_address, + route_match_ip_address_compile, + route_match_ip_address_free +}; + +/* `match ip address prefix-list PREFIX_LIST' */ +static enum route_map_cmd_result_t +route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix, + void *object) +{ + struct prefix_list *plist; + + plist = prefix_list_lookup(AFI_IP, (char *)rule); + if (plist == NULL) { + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) + zlog_debug( + "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", + __func__, (char *)rule); + + return RMAP_NOMATCH; + } + + return (prefix_list_apply(plist, prefix) == PREFIX_DENY ? RMAP_NOMATCH + : RMAP_MATCH); +} + +static void *route_match_ip_address_prefix_list_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void route_match_ip_address_prefix_list_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static const struct route_map_rule_cmd + route_match_ip_address_prefix_list_cmd = { + "ip address prefix-list", + route_match_ip_address_prefix_list, + route_match_ip_address_prefix_list_compile, + route_match_ip_address_prefix_list_free +}; + +/* `match interface IFNAME' */ +/* Match function should return 1 if match is success else return + zero. */ +static enum route_map_cmd_result_t +route_match_interface(void *rule, const struct prefix *prefix, void *object) +{ + struct interface *ifp; + struct external_info *ei; + + ei = object; + ifp = if_lookup_by_name((char *)rule, ei->ospf->vrf_id); + + if (ifp == NULL || ifp->ifindex != ei->ifindex) + return RMAP_NOMATCH; + + return RMAP_MATCH; +} + +/* Route map `interface' match statement. `arg' should be + interface name. */ +static void *route_match_interface_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `interface' value. */ +static void route_match_interface_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for ip address matching. */ +static const struct route_map_rule_cmd route_match_interface_cmd = { + "interface", + route_match_interface, + route_match_interface_compile, + route_match_interface_free +}; + +/* Match function return 1 if match is success else return zero. */ +static enum route_map_cmd_result_t +route_match_tag(void *rule, const struct prefix *prefix, void *object) +{ + route_tag_t *tag; + struct external_info *ei; + + tag = rule; + ei = object; + + return ((ei->tag == *tag) ? RMAP_MATCH : RMAP_NOMATCH); +} + +/* Route map commands for tag matching. */ +static const struct route_map_rule_cmd route_match_tag_cmd = { + "tag", + route_match_tag, + route_map_rule_tag_compile, + route_map_rule_tag_free, +}; + +struct ospf_metric { + enum { metric_increment, metric_decrement, metric_absolute } type; + bool used; + uint32_t metric; +}; + +/* `set metric METRIC' */ +/* Set metric to attribute. */ +static enum route_map_cmd_result_t +route_set_metric(void *rule, const struct prefix *prefix, void *object) +{ + struct ospf_metric *metric; + struct external_info *ei; + + /* Fetch routemap's rule information. */ + metric = rule; + ei = object; + + /* Set metric out value. */ + if (!metric->used) + return RMAP_OKAY; + + ROUTEMAP_METRIC(ei) = ei->metric; + + if (metric->type == metric_increment) + ROUTEMAP_METRIC(ei) += metric->metric; + else if (metric->type == metric_decrement) + ROUTEMAP_METRIC(ei) -= metric->metric; + else if (metric->type == metric_absolute) + ROUTEMAP_METRIC(ei) = metric->metric; + + if ((uint32_t)ROUTEMAP_METRIC(ei) < ei->min_metric) + ROUTEMAP_METRIC(ei) = ei->min_metric; + if ((uint32_t)ROUTEMAP_METRIC(ei) > ei->max_metric) + ROUTEMAP_METRIC(ei) = ei->max_metric; + + return RMAP_OKAY; +} + +/* set metric compilation. */ +static void *route_set_metric_compile(const char *arg) +{ + struct ospf_metric *metric; + + metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(*metric)); + metric->used = false; + + if (all_digit(arg)) + metric->type = metric_absolute; + + if (strmatch(arg, "+rtt") || strmatch(arg, "-rtt")) { + flog_warn(EC_OSPF_SET_METRIC_PLUS, + "OSPF does not support 'set metric +rtt / -rtt'"); + return metric; + } + + if ((arg[0] == '+') && all_digit(arg + 1)) { + metric->type = metric_increment; + arg++; + } + + if ((arg[0] == '-') && all_digit(arg + 1)) { + metric->type = metric_decrement; + arg++; + } + + metric->metric = strtoul(arg, NULL, 10); + + if (metric->metric) + metric->used = true; + + return metric; +} + +/* Free route map's compiled `set metric' value. */ +static void route_set_metric_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Set metric rule structure. */ +static const struct route_map_rule_cmd route_set_metric_cmd = { + "metric", + route_set_metric, + route_set_metric_compile, + route_set_metric_free, +}; + +/* `set min-metric METRIC' */ +/* Set min-metric to attribute. */ +static enum route_map_cmd_result_t +route_set_min_metric(void *rule, const struct prefix *prefix, void *object) +{ + uint32_t *min_metric; + struct external_info *ei; + + /* Fetch routemap's rule information. */ + min_metric = rule; + ei = object; + + ei->min_metric = *min_metric; + + if (ei->min_metric > OSPF_LS_INFINITY) + ei->min_metric = OSPF_LS_INFINITY; + + if ((uint32_t)ROUTEMAP_METRIC(ei) < ei->min_metric) + ROUTEMAP_METRIC(ei) = ei->min_metric; + + return RMAP_OKAY; +} + +/* set min-metric compilation. */ +static void *route_set_min_metric_compile(const char *arg) +{ + + uint32_t *min_metric; + + min_metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t)); + + *min_metric = strtoul(arg, NULL, 10); + + if (*min_metric) + return min_metric; + + XFREE(MTYPE_ROUTE_MAP_COMPILED, min_metric); + return NULL; +} + +/* Free route map's compiled `set min-metric' value. */ +static void route_set_min_metric_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Set metric rule structure. */ +static const struct route_map_rule_cmd route_set_min_metric_cmd = { + "min-metric", + route_set_min_metric, + route_set_min_metric_compile, + route_set_min_metric_free, +}; + + +/* `set max-metric METRIC' */ +/* Set max-metric to attribute. */ +static enum route_map_cmd_result_t +route_set_max_metric(void *rule, const struct prefix *prefix, void *object) +{ + uint32_t *max_metric; + struct external_info *ei; + + /* Fetch routemap's rule information. */ + max_metric = rule; + ei = object; + + ei->max_metric = *max_metric; + + if (ei->max_metric > OSPF_LS_INFINITY) + ei->max_metric = OSPF_LS_INFINITY; + + if ((uint32_t)ROUTEMAP_METRIC(ei) > ei->max_metric) + ROUTEMAP_METRIC(ei) = ei->max_metric; + + return RMAP_OKAY; +} + +/* set max-metric compilation. */ +static void *route_set_max_metric_compile(const char *arg) +{ + + uint32_t *max_metric; + + max_metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t)); + + *max_metric = strtoul(arg, NULL, 10); + + if (*max_metric) + return max_metric; + + XFREE(MTYPE_ROUTE_MAP_COMPILED, max_metric); + return NULL; +} + +/* Free route map's compiled `set max-metric' value. */ +static void route_set_max_metric_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Set metric rule structure. */ +static const struct route_map_rule_cmd route_set_max_metric_cmd = { + "max-metric", + route_set_max_metric, + route_set_max_metric_compile, + route_set_max_metric_free, +}; + +/* `set metric-type TYPE' */ +/* Set metric-type to attribute. */ +static enum route_map_cmd_result_t +route_set_metric_type(void *rule, const struct prefix *prefix, void *object) +{ + uint32_t *metric_type; + struct external_info *ei; + + /* Fetch routemap's rule information. */ + metric_type = rule; + ei = object; + + /* Set metric out value. */ + ROUTEMAP_METRIC_TYPE(ei) = *metric_type; + + return RMAP_OKAY; +} + +/* set metric-type compilation. */ +static void *route_set_metric_type_compile(const char *arg) +{ + uint32_t *metric_type; + + metric_type = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t)); + if (strcmp(arg, "type-1") == 0) + *metric_type = EXTERNAL_METRIC_TYPE_1; + else if (strcmp(arg, "type-2") == 0) + *metric_type = EXTERNAL_METRIC_TYPE_2; + + if (*metric_type == EXTERNAL_METRIC_TYPE_1 + || *metric_type == EXTERNAL_METRIC_TYPE_2) + return metric_type; + + XFREE(MTYPE_ROUTE_MAP_COMPILED, metric_type); + return NULL; +} + +/* Free route map's compiled `set metric-type' value. */ +static void route_set_metric_type_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Set metric rule structure. */ +static const struct route_map_rule_cmd route_set_metric_type_cmd = { + "metric-type", + route_set_metric_type, + route_set_metric_type_compile, + route_set_metric_type_free, +}; + +static enum route_map_cmd_result_t +route_set_tag(void *rule, const struct prefix *prefix, void *object) +{ + route_tag_t *tag; + struct external_info *ei; + + tag = rule; + ei = object; + + /* Set tag value */ + ei->tag = *tag; + + return RMAP_OKAY; +} + +/* Route map commands for tag set. */ +static const struct route_map_rule_cmd route_set_tag_cmd = { + "tag", + route_set_tag, + route_map_rule_tag_compile, + route_map_rule_tag_free, +}; + +DEFUN_YANG (set_metric_type, + set_metric_type_cmd, + "set metric-type ", + SET_STR + "Type of metric for destination routing protocol\n" + "OSPF[6] external type 1 metric\n" + "OSPF[6] external type 2 metric\n") +{ + char *ext = argv[2]->text; + + const char *xpath = + "./set-action[action='frr-ospf-route-map:metric-type']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-ospf-route-map:metric-type", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, ext); + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_set_metric_type, + no_set_metric_type_cmd, + "no set metric-type []", + NO_STR + SET_STR + "Type of metric for destination routing protocol\n" + "OSPF[6] external type 1 metric\n" + "OSPF[6] external type 2 metric\n") +{ + const char *xpath = + "./set-action[action='frr-ospf-route-map:metric-type']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +/* Route-map init */ +void ospf_route_map_init(void) +{ + route_map_init(); + + route_map_add_hook(ospf_route_map_update); + route_map_delete_hook(ospf_route_map_update); + route_map_event_hook(ospf_route_map_event); + + route_map_match_ip_next_hop_hook(generic_match_add); + route_map_no_match_ip_next_hop_hook(generic_match_delete); + + route_map_match_interface_hook(generic_match_add); + route_map_no_match_interface_hook(generic_match_delete); + + route_map_match_ip_address_hook(generic_match_add); + route_map_no_match_ip_address_hook(generic_match_delete); + + route_map_match_ip_address_prefix_list_hook(generic_match_add); + route_map_no_match_ip_address_prefix_list_hook(generic_match_delete); + + route_map_match_ip_next_hop_prefix_list_hook(generic_match_add); + route_map_no_match_ip_next_hop_prefix_list_hook(generic_match_delete); + + route_map_match_ip_next_hop_type_hook(generic_match_add); + route_map_no_match_ip_next_hop_type_hook(generic_match_delete); + + route_map_match_tag_hook(generic_match_add); + route_map_no_match_tag_hook(generic_match_delete); + + route_map_set_metric_hook(generic_set_add); + route_map_no_set_metric_hook(generic_set_delete); + + route_map_set_min_metric_hook(generic_set_add); + route_map_no_set_min_metric_hook(generic_set_delete); + + route_map_set_max_metric_hook(generic_set_add); + route_map_no_set_max_metric_hook(generic_set_delete); + + route_map_set_tag_hook(generic_set_add); + route_map_no_set_tag_hook(generic_set_delete); + + route_map_install_match(&route_match_ip_nexthop_cmd); + route_map_install_match(&route_match_ip_next_hop_prefix_list_cmd); + route_map_install_match(&route_match_ip_address_cmd); + route_map_install_match(&route_match_ip_address_prefix_list_cmd); + route_map_install_match(&route_match_ip_next_hop_type_cmd); + route_map_install_match(&route_match_interface_cmd); + route_map_install_match(&route_match_tag_cmd); + + route_map_install_set(&route_set_metric_cmd); + route_map_install_set(&route_set_min_metric_cmd); + route_map_install_set(&route_set_max_metric_cmd); + route_map_install_set(&route_set_metric_type_cmd); + route_map_install_set(&route_set_tag_cmd); + + install_element(RMAP_NODE, &set_metric_type_cmd); + install_element(RMAP_NODE, &no_set_metric_type_cmd); +} diff --git a/ospfd/ospf_routemap_nb.c b/ospfd/ospf_routemap_nb.c new file mode 100644 index 0000000..8ccd0e5 --- /dev/null +++ b/ospfd/ospf_routemap_nb.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020 Vmware + * Sarita Patra + */ + +#include + +#include "lib/northbound.h" +#include "lib/routemap.h" +#include "ospf_routemap_nb.h" + +/* clang-format off */ +const struct frr_yang_module_info frr_ospf_route_map_info = { + .name = "frr-ospf-route-map", + .nodes = { + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-ospf-route-map:metric-type", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_metric_type_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_metric_type_destroy, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/ospfd/ospf_routemap_nb.h b/ospfd/ospf_routemap_nb.h new file mode 100644 index 0000000..5bba784 --- /dev/null +++ b/ospfd/ospf_routemap_nb.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020 Vmware + * Sarita Patra + */ + +#ifndef _FRR_OSPF_ROUTEMAP_NB_H_ +#define _FRR_OSPF_ROUTEMAP_NB_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct frr_yang_module_info frr_ospf_route_map_info; + +/* prototypes */ +int lib_route_map_entry_set_action_rmap_set_action_metric_type_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_metric_type_destroy(struct nb_cb_destroy_args *args); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ospfd/ospf_routemap_nb_config.c b/ospfd/ospf_routemap_nb_config.c new file mode 100644 index 0000000..a35a1ef --- /dev/null +++ b/ospfd/ospf_routemap_nb_config.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020 Vmware + * Sarita Patra + */ + +#include + +#include "lib/command.h" +#include "lib/log.h" +#include "lib/northbound.h" +#include "lib/routemap.h" +#include "ospf_routemap_nb.h" + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-ospf-route-map:metric-type + */ +int lib_route_map_entry_set_action_rmap_set_action_metric_type_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "metric-type"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "metric-type", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_metric_type_destroy( + struct nb_cb_destroy_args *args) +{ + return lib_route_map_entry_set_destroy(args); +} diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c new file mode 100644 index 0000000..c9aaa9f --- /dev/null +++ b/ospfd/ospf_snmp.c @@ -0,0 +1,2553 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* OSPFv2 SNMP support + * Copyright (C) 2005 6WIND + * Copyright (C) 2000 IP Infusion Inc. + * + * Written by Kunihiro Ishiguro + */ + +#include + +#include +#include + +#include "if.h" +#include "log.h" +#include "prefix.h" +#include "table.h" +#include "command.h" +#include "memory.h" +#include "smux.h" +#include "libfrr.h" +#include "lib/version.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_zebra.h" + +DEFINE_MTYPE_STATIC(OSPFD, SNMP, "OSPF SNMP"); + +/* OSPF2-MIB. */ +#define OSPF2MIB 1,3,6,1,2,1,14 + +/* OSPF MIB General Group values. */ +#define OSPFROUTERID 1 +#define OSPFADMINSTAT 2 +#define OSPFVERSIONNUMBER 3 +#define OSPFAREABDRRTRSTATUS 4 +#define OSPFASBDRRTRSTATUS 5 +#define OSPFEXTERNLSACOUNT 6 +#define OSPFEXTERNLSACKSUMSUM 7 +#define OSPFTOSSUPPORT 8 +#define OSPFORIGINATENEWLSAS 9 +#define OSPFRXNEWLSAS 10 +#define OSPFEXTLSDBLIMIT 11 +#define OSPFMULTICASTEXTENSIONS 12 +#define OSPFEXITOVERFLOWINTERVAL 13 +#define OSPFDEMANDEXTENSIONS 14 + +/* OSPF MIB ospfAreaTable. */ +#define OSPFAREAID 1 +#define OSPFAUTHTYPE 2 +#define OSPFIMPORTASEXTERN 3 +#define OSPFSPFRUNS 4 +#define OSPFAREABDRRTRCOUNT 5 +#define OSPFASBDRRTRCOUNT 6 +#define OSPFAREALSACOUNT 7 +#define OSPFAREALSACKSUMSUM 8 +#define OSPFAREASUMMARY 9 +#define OSPFAREASTATUS 10 + +/* OSPF MIB ospfStubAreaTable. */ +#define OSPFSTUBAREAID 1 +#define OSPFSTUBTOS 2 +#define OSPFSTUBMETRIC 3 +#define OSPFSTUBSTATUS 4 +#define OSPFSTUBMETRICTYPE 5 + +/* OSPF MIB ospfLsdbTable. */ +#define OSPFLSDBAREAID 1 +#define OSPFLSDBTYPE 2 +#define OSPFLSDBLSID 3 +#define OSPFLSDBROUTERID 4 +#define OSPFLSDBSEQUENCE 5 +#define OSPFLSDBAGE 6 +#define OSPFLSDBCHECKSUM 7 +#define OSPFLSDBADVERTISEMENT 8 + +/* OSPF MIB ospfAreaRangeTable. */ +#define OSPFAREARANGEAREAID 1 +#define OSPFAREARANGENET 2 +#define OSPFAREARANGEMASK 3 +#define OSPFAREARANGESTATUS 4 +#define OSPFAREARANGEEFFECT 5 + +/* OSPF MIB ospfHostTable. */ +#define OSPFHOSTIPADDRESS 1 +#define OSPFHOSTTOS 2 +#define OSPFHOSTMETRIC 3 +#define OSPFHOSTSTATUS 4 +#define OSPFHOSTAREAID 5 + +/* OSPF MIB ospfIfTable. */ +#define OSPFIFIPADDRESS 1 +#define OSPFADDRESSLESSIF 2 +#define OSPFIFAREAID 3 +#define OSPFIFTYPE 4 +#define OSPFIFADMINSTAT 5 +#define OSPFIFRTRPRIORITY 6 +#define OSPFIFTRANSITDELAY 7 +#define OSPFIFRETRANSINTERVAL 8 +#define OSPFIFHELLOINTERVAL 9 +#define OSPFIFRTRDEADINTERVAL 10 +#define OSPFIFPOLLINTERVAL 11 +#define OSPFIFSTATE 12 +#define OSPFIFDESIGNATEDROUTER 13 +#define OSPFIFBACKUPDESIGNATEDROUTER 14 +#define OSPFIFEVENTS 15 +#define OSPFIFAUTHKEY 16 +#define OSPFIFSTATUS 17 +#define OSPFIFMULTICASTFORWARDING 18 +#define OSPFIFDEMAND 19 +#define OSPFIFAUTHTYPE 20 + +/* OSPF MIB ospfIfMetricTable. */ +#define OSPFIFMETRICIPADDRESS 1 +#define OSPFIFMETRICADDRESSLESSIF 2 +#define OSPFIFMETRICTOS 3 +#define OSPFIFMETRICVALUE 4 +#define OSPFIFMETRICSTATUS 5 + +/* OSPF MIB ospfVirtIfTable. */ +#define OSPFVIRTIFAREAID 1 +#define OSPFVIRTIFNEIGHBOR 2 +#define OSPFVIRTIFTRANSITDELAY 3 +#define OSPFVIRTIFRETRANSINTERVAL 4 +#define OSPFVIRTIFHELLOINTERVAL 5 +#define OSPFVIRTIFRTRDEADINTERVAL 6 +#define OSPFVIRTIFSTATE 7 +#define OSPFVIRTIFEVENTS 8 +#define OSPFVIRTIFAUTHKEY 9 +#define OSPFVIRTIFSTATUS 10 +#define OSPFVIRTIFAUTHTYPE 11 + +/* OSPF MIB ospfNbrTable. */ +#define OSPFNBRIPADDR 1 +#define OSPFNBRADDRESSLESSINDEX 2 +#define OSPFNBRRTRID 3 +#define OSPFNBROPTIONS 4 +#define OSPFNBRPRIORITY 5 +#define OSPFNBRSTATE 6 +#define OSPFNBREVENTS 7 +#define OSPFNBRLSRETRANSQLEN 8 +#define OSPFNBMANBRSTATUS 9 +#define OSPFNBMANBRPERMANENCE 10 +#define OSPFNBRHELLOSUPPRESSED 11 + +/* OSPF MIB ospfVirtNbrTable. */ +#define OSPFVIRTNBRAREA 1 +#define OSPFVIRTNBRRTRID 2 +#define OSPFVIRTNBRIPADDR 3 +#define OSPFVIRTNBROPTIONS 4 +#define OSPFVIRTNBRSTATE 5 +#define OSPFVIRTNBREVENTS 6 +#define OSPFVIRTNBRLSRETRANSQLEN 7 +#define OSPFVIRTNBRHELLOSUPPRESSED 8 + +/* OSPF MIB ospfExtLsdbTable. */ +#define OSPFEXTLSDBTYPE 1 +#define OSPFEXTLSDBLSID 2 +#define OSPFEXTLSDBROUTERID 3 +#define OSPFEXTLSDBSEQUENCE 4 +#define OSPFEXTLSDBAGE 5 +#define OSPFEXTLSDBCHECKSUM 6 +#define OSPFEXTLSDBADVERTISEMENT 7 + +/* OSPF MIB ospfAreaAggregateTable. */ +#define OSPFAREAAGGREGATEAREAID 1 +#define OSPFAREAAGGREGATELSDBTYPE 2 +#define OSPFAREAAGGREGATENET 3 +#define OSPFAREAAGGREGATEMASK 4 +#define OSPFAREAAGGREGATESTATUS 5 +#define OSPFAREAAGGREGATEEFFECT 6 + +/* SYNTAX Status from OSPF-MIB. */ +#define OSPF_STATUS_ENABLED 1 +#define OSPF_STATUS_DISABLED 2 + +/* SNMP value hack. */ +#define COUNTER ASN_COUNTER +#define INTEGER ASN_INTEGER +#define GAUGE ASN_GAUGE +#define TIMETICKS ASN_TIMETICKS +#define IPADDRESS ASN_IPADDRESS +#define STRING ASN_OCTET_STR + +/* Because DR/DROther values are exhanged wrt RFC */ +#define ISM_SNMP(x) \ + (((x) == ISM_DROther) ? ISM_DR : ((x) == ISM_DR) ? ISM_DROther : (x)) + +/* Declare static local variables for convenience. */ +SNMP_LOCAL_VARIABLES + +/* OSPF-MIB instances. */ +static oid ospf_oid[] = {OSPF2MIB}; +static oid ospf_trap_oid[] = {OSPF2MIB, 16, 2}; /* Not reverse mappable! */ + +/* IP address 0.0.0.0. */ +static struct in_addr ospf_empty_addr = {.s_addr = 0}; + +/* Hook functions. */ +static uint8_t *ospfGeneralGroup(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static uint8_t *ospfAreaEntry(struct variable *, oid *, size_t *, int, size_t *, + WriteMethod **); +static uint8_t *ospfStubAreaEntry(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static uint8_t *ospfLsdbEntry(struct variable *, oid *, size_t *, int, size_t *, + WriteMethod **); +static uint8_t *ospfAreaRangeEntry(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static uint8_t *ospfHostEntry(struct variable *, oid *, size_t *, int, size_t *, + WriteMethod **); +static uint8_t *ospfIfEntry(struct variable *, oid *, size_t *, int, size_t *, + WriteMethod **); +static uint8_t *ospfIfMetricEntry(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static uint8_t *ospfVirtIfEntry(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static uint8_t *ospfNbrEntry(struct variable *, oid *, size_t *, int, size_t *, + WriteMethod **); +static uint8_t *ospfVirtNbrEntry(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static uint8_t *ospfExtLsdbEntry(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static uint8_t *ospfAreaAggregateEntry(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); + +static struct variable ospf_variables[] = { + /* OSPF general variables */ + {OSPFROUTERID, IPADDRESS, RWRITE, ospfGeneralGroup, 2, {1, 1}}, + {OSPFADMINSTAT, INTEGER, RWRITE, ospfGeneralGroup, 2, {1, 2}}, + {OSPFVERSIONNUMBER, INTEGER, RONLY, ospfGeneralGroup, 2, {1, 3}}, + {OSPFAREABDRRTRSTATUS, INTEGER, RONLY, ospfGeneralGroup, 2, {1, 4}}, + {OSPFASBDRRTRSTATUS, INTEGER, RWRITE, ospfGeneralGroup, 2, {1, 5}}, + {OSPFEXTERNLSACOUNT, GAUGE, RONLY, ospfGeneralGroup, 2, {1, 6}}, + {OSPFEXTERNLSACKSUMSUM, INTEGER, RONLY, ospfGeneralGroup, 2, {1, 7}}, + {OSPFTOSSUPPORT, INTEGER, RWRITE, ospfGeneralGroup, 2, {1, 8}}, + {OSPFORIGINATENEWLSAS, COUNTER, RONLY, ospfGeneralGroup, 2, {1, 9}}, + {OSPFRXNEWLSAS, COUNTER, RONLY, ospfGeneralGroup, 2, {1, 10}}, + {OSPFEXTLSDBLIMIT, INTEGER, RWRITE, ospfGeneralGroup, 2, {1, 11}}, + {OSPFMULTICASTEXTENSIONS, + INTEGER, + RWRITE, + ospfGeneralGroup, + 2, + {1, 12}}, + {OSPFEXITOVERFLOWINTERVAL, + INTEGER, + RWRITE, + ospfGeneralGroup, + 2, + {1, 13}}, + {OSPFDEMANDEXTENSIONS, INTEGER, RWRITE, ospfGeneralGroup, 2, {1, 14}}, + + /* OSPF area data structure. */ + {OSPFAREAID, IPADDRESS, RONLY, ospfAreaEntry, 3, {2, 1, 1}}, + {OSPFAUTHTYPE, INTEGER, RWRITE, ospfAreaEntry, 3, {2, 1, 2}}, + {OSPFIMPORTASEXTERN, INTEGER, RWRITE, ospfAreaEntry, 3, {2, 1, 3}}, + {OSPFSPFRUNS, COUNTER, RONLY, ospfAreaEntry, 3, {2, 1, 4}}, + {OSPFAREABDRRTRCOUNT, GAUGE, RONLY, ospfAreaEntry, 3, {2, 1, 5}}, + {OSPFASBDRRTRCOUNT, GAUGE, RONLY, ospfAreaEntry, 3, {2, 1, 6}}, + {OSPFAREALSACOUNT, GAUGE, RONLY, ospfAreaEntry, 3, {2, 1, 7}}, + {OSPFAREALSACKSUMSUM, INTEGER, RONLY, ospfAreaEntry, 3, {2, 1, 8}}, + {OSPFAREASUMMARY, INTEGER, RWRITE, ospfAreaEntry, 3, {2, 1, 9}}, + {OSPFAREASTATUS, INTEGER, RWRITE, ospfAreaEntry, 3, {2, 1, 10}}, + + /* OSPF stub area information. */ + {OSPFSTUBAREAID, IPADDRESS, RONLY, ospfStubAreaEntry, 3, {3, 1, 1}}, + {OSPFSTUBTOS, INTEGER, RONLY, ospfStubAreaEntry, 3, {3, 1, 2}}, + {OSPFSTUBMETRIC, INTEGER, RWRITE, ospfStubAreaEntry, 3, {3, 1, 3}}, + {OSPFSTUBSTATUS, INTEGER, RWRITE, ospfStubAreaEntry, 3, {3, 1, 4}}, + {OSPFSTUBMETRICTYPE, INTEGER, RWRITE, ospfStubAreaEntry, 3, {3, 1, 5}}, + + /* OSPF link state database. */ + {OSPFLSDBAREAID, IPADDRESS, RONLY, ospfLsdbEntry, 3, {4, 1, 1}}, + {OSPFLSDBTYPE, INTEGER, RONLY, ospfLsdbEntry, 3, {4, 1, 2}}, + {OSPFLSDBLSID, IPADDRESS, RONLY, ospfLsdbEntry, 3, {4, 1, 3}}, + {OSPFLSDBROUTERID, IPADDRESS, RONLY, ospfLsdbEntry, 3, {4, 1, 4}}, + {OSPFLSDBSEQUENCE, INTEGER, RONLY, ospfLsdbEntry, 3, {4, 1, 5}}, + {OSPFLSDBAGE, INTEGER, RONLY, ospfLsdbEntry, 3, {4, 1, 6}}, + {OSPFLSDBCHECKSUM, INTEGER, RONLY, ospfLsdbEntry, 3, {4, 1, 7}}, + {OSPFLSDBADVERTISEMENT, STRING, RONLY, ospfLsdbEntry, 3, {4, 1, 8}}, + + /* Area range table. */ + {OSPFAREARANGEAREAID, + IPADDRESS, + RONLY, + ospfAreaRangeEntry, + 3, + {5, 1, 1}}, + {OSPFAREARANGENET, IPADDRESS, RONLY, ospfAreaRangeEntry, 3, {5, 1, 2}}, + {OSPFAREARANGEMASK, + IPADDRESS, + RWRITE, + ospfAreaRangeEntry, + 3, + {5, 1, 3}}, + {OSPFAREARANGESTATUS, + INTEGER, + RWRITE, + ospfAreaRangeEntry, + 3, + {5, 1, 4}}, + {OSPFAREARANGEEFFECT, + INTEGER, + RWRITE, + ospfAreaRangeEntry, + 3, + {5, 1, 5}}, + + /* OSPF host table. */ + {OSPFHOSTIPADDRESS, IPADDRESS, RONLY, ospfHostEntry, 3, {6, 1, 1}}, + {OSPFHOSTTOS, INTEGER, RONLY, ospfHostEntry, 3, {6, 1, 2}}, + {OSPFHOSTMETRIC, INTEGER, RWRITE, ospfHostEntry, 3, {6, 1, 3}}, + {OSPFHOSTSTATUS, INTEGER, RWRITE, ospfHostEntry, 3, {6, 1, 4}}, + {OSPFHOSTAREAID, IPADDRESS, RONLY, ospfHostEntry, 3, {6, 1, 5}}, + + /* OSPF interface table. */ + {OSPFIFIPADDRESS, IPADDRESS, RONLY, ospfIfEntry, 3, {7, 1, 1}}, + {OSPFADDRESSLESSIF, INTEGER, RONLY, ospfIfEntry, 3, {7, 1, 2}}, + {OSPFIFAREAID, IPADDRESS, RWRITE, ospfIfEntry, 3, {7, 1, 3}}, + {OSPFIFTYPE, INTEGER, RWRITE, ospfIfEntry, 3, {7, 1, 4}}, + {OSPFIFADMINSTAT, INTEGER, RWRITE, ospfIfEntry, 3, {7, 1, 5}}, + {OSPFIFRTRPRIORITY, INTEGER, RWRITE, ospfIfEntry, 3, {7, 1, 6}}, + {OSPFIFTRANSITDELAY, INTEGER, RWRITE, ospfIfEntry, 3, {7, 1, 7}}, + {OSPFIFRETRANSINTERVAL, INTEGER, RWRITE, ospfIfEntry, 3, {7, 1, 8}}, + {OSPFIFHELLOINTERVAL, INTEGER, RWRITE, ospfIfEntry, 3, {7, 1, 9}}, + {OSPFIFRTRDEADINTERVAL, INTEGER, RWRITE, ospfIfEntry, 3, {7, 1, 10}}, + {OSPFIFPOLLINTERVAL, INTEGER, RWRITE, ospfIfEntry, 3, {7, 1, 11}}, + {OSPFIFSTATE, INTEGER, RONLY, ospfIfEntry, 3, {7, 1, 12}}, + {OSPFIFDESIGNATEDROUTER, IPADDRESS, RONLY, ospfIfEntry, 3, {7, 1, 13}}, + {OSPFIFBACKUPDESIGNATEDROUTER, + IPADDRESS, + RONLY, + ospfIfEntry, + 3, + {7, 1, 14}}, + {OSPFIFEVENTS, COUNTER, RONLY, ospfIfEntry, 3, {7, 1, 15}}, + {OSPFIFAUTHKEY, STRING, RWRITE, ospfIfEntry, 3, {7, 1, 16}}, + {OSPFIFSTATUS, INTEGER, RWRITE, ospfIfEntry, 3, {7, 1, 17}}, + {OSPFIFMULTICASTFORWARDING, + INTEGER, + RWRITE, + ospfIfEntry, + 3, + {7, 1, 18}}, + {OSPFIFDEMAND, INTEGER, RWRITE, ospfIfEntry, 3, {7, 1, 19}}, + {OSPFIFAUTHTYPE, INTEGER, RWRITE, ospfIfEntry, 3, {7, 1, 20}}, + + /* OSPF interface metric table. */ + {OSPFIFMETRICIPADDRESS, + IPADDRESS, + RONLY, + ospfIfMetricEntry, + 3, + {8, 1, 1}}, + {OSPFIFMETRICADDRESSLESSIF, + INTEGER, + RONLY, + ospfIfMetricEntry, + 3, + {8, 1, 2}}, + {OSPFIFMETRICTOS, INTEGER, RONLY, ospfIfMetricEntry, 3, {8, 1, 3}}, + {OSPFIFMETRICVALUE, INTEGER, RWRITE, ospfIfMetricEntry, 3, {8, 1, 4}}, + {OSPFIFMETRICSTATUS, INTEGER, RWRITE, ospfIfMetricEntry, 3, {8, 1, 5}}, + + /* OSPF virtual interface table. */ + {OSPFVIRTIFAREAID, IPADDRESS, RONLY, ospfVirtIfEntry, 3, {9, 1, 1}}, + {OSPFVIRTIFNEIGHBOR, IPADDRESS, RONLY, ospfVirtIfEntry, 3, {9, 1, 2}}, + {OSPFVIRTIFTRANSITDELAY, + INTEGER, + RWRITE, + ospfVirtIfEntry, + 3, + {9, 1, 3}}, + {OSPFVIRTIFRETRANSINTERVAL, + INTEGER, + RWRITE, + ospfVirtIfEntry, + 3, + {9, 1, 4}}, + {OSPFVIRTIFHELLOINTERVAL, + INTEGER, + RWRITE, + ospfVirtIfEntry, + 3, + {9, 1, 5}}, + {OSPFVIRTIFRTRDEADINTERVAL, + INTEGER, + RWRITE, + ospfVirtIfEntry, + 3, + {9, 1, 6}}, + {OSPFVIRTIFSTATE, INTEGER, RONLY, ospfVirtIfEntry, 3, {9, 1, 7}}, + {OSPFVIRTIFEVENTS, COUNTER, RONLY, ospfVirtIfEntry, 3, {9, 1, 8}}, + {OSPFVIRTIFAUTHKEY, STRING, RWRITE, ospfVirtIfEntry, 3, {9, 1, 9}}, + {OSPFVIRTIFSTATUS, INTEGER, RWRITE, ospfVirtIfEntry, 3, {9, 1, 10}}, + {OSPFVIRTIFAUTHTYPE, INTEGER, RWRITE, ospfVirtIfEntry, 3, {9, 1, 11}}, + + /* OSPF neighbor table. */ + {OSPFNBRIPADDR, IPADDRESS, RONLY, ospfNbrEntry, 3, {10, 1, 1}}, + {OSPFNBRADDRESSLESSINDEX, INTEGER, RONLY, ospfNbrEntry, 3, {10, 1, 2}}, + {OSPFNBRRTRID, IPADDRESS, RONLY, ospfNbrEntry, 3, {10, 1, 3}}, + {OSPFNBROPTIONS, INTEGER, RONLY, ospfNbrEntry, 3, {10, 1, 4}}, + {OSPFNBRPRIORITY, INTEGER, RWRITE, ospfNbrEntry, 3, {10, 1, 5}}, + {OSPFNBRSTATE, INTEGER, RONLY, ospfNbrEntry, 3, {10, 1, 6}}, + {OSPFNBREVENTS, COUNTER, RONLY, ospfNbrEntry, 3, {10, 1, 7}}, + {OSPFNBRLSRETRANSQLEN, GAUGE, RONLY, ospfNbrEntry, 3, {10, 1, 8}}, + {OSPFNBMANBRSTATUS, INTEGER, RWRITE, ospfNbrEntry, 3, {10, 1, 9}}, + {OSPFNBMANBRPERMANENCE, INTEGER, RONLY, ospfNbrEntry, 3, {10, 1, 10}}, + {OSPFNBRHELLOSUPPRESSED, INTEGER, RONLY, ospfNbrEntry, 3, {10, 1, 11}}, + + /* OSPF virtual neighbor table. */ + {OSPFVIRTNBRAREA, IPADDRESS, RONLY, ospfVirtNbrEntry, 3, {11, 1, 1}}, + {OSPFVIRTNBRRTRID, IPADDRESS, RONLY, ospfVirtNbrEntry, 3, {11, 1, 2}}, + {OSPFVIRTNBRIPADDR, IPADDRESS, RONLY, ospfVirtNbrEntry, 3, {11, 1, 3}}, + {OSPFVIRTNBROPTIONS, INTEGER, RONLY, ospfVirtNbrEntry, 3, {11, 1, 4}}, + {OSPFVIRTNBRSTATE, INTEGER, RONLY, ospfVirtNbrEntry, 3, {11, 1, 5}}, + {OSPFVIRTNBREVENTS, COUNTER, RONLY, ospfVirtNbrEntry, 3, {11, 1, 6}}, + {OSPFVIRTNBRLSRETRANSQLEN, + INTEGER, + RONLY, + ospfVirtNbrEntry, + 3, + {11, 1, 7}}, + {OSPFVIRTNBRHELLOSUPPRESSED, + INTEGER, + RONLY, + ospfVirtNbrEntry, + 3, + {11, 1, 8}}, + + /* OSPF link state database, external. */ + {OSPFEXTLSDBTYPE, INTEGER, RONLY, ospfExtLsdbEntry, 3, {12, 1, 1}}, + {OSPFEXTLSDBLSID, IPADDRESS, RONLY, ospfExtLsdbEntry, 3, {12, 1, 2}}, + {OSPFEXTLSDBROUTERID, + IPADDRESS, + RONLY, + ospfExtLsdbEntry, + 3, + {12, 1, 3}}, + {OSPFEXTLSDBSEQUENCE, INTEGER, RONLY, ospfExtLsdbEntry, 3, {12, 1, 4}}, + {OSPFEXTLSDBAGE, INTEGER, RONLY, ospfExtLsdbEntry, 3, {12, 1, 5}}, + {OSPFEXTLSDBCHECKSUM, INTEGER, RONLY, ospfExtLsdbEntry, 3, {12, 1, 6}}, + {OSPFEXTLSDBADVERTISEMENT, + STRING, + RONLY, + ospfExtLsdbEntry, + 3, + {12, 1, 7}}, + + /* OSPF area aggregate table. */ + {OSPFAREAAGGREGATEAREAID, + IPADDRESS, + RONLY, + ospfAreaAggregateEntry, + 3, + {14, 1, 1}}, + {OSPFAREAAGGREGATELSDBTYPE, + INTEGER, + RONLY, + ospfAreaAggregateEntry, + 3, + {14, 1, 2}}, + {OSPFAREAAGGREGATENET, + IPADDRESS, + RONLY, + ospfAreaAggregateEntry, + 3, + {14, 1, 3}}, + {OSPFAREAAGGREGATEMASK, + IPADDRESS, + RONLY, + ospfAreaAggregateEntry, + 3, + {14, 1, 4}}, + {OSPFAREAAGGREGATESTATUS, + INTEGER, + RWRITE, + ospfAreaAggregateEntry, + 3, + {14, 1, 5}}, + {OSPFAREAAGGREGATEEFFECT, + INTEGER, + RWRITE, + ospfAreaAggregateEntry, + 3, + {14, 1, 6}}}; + +/* The administrative status of OSPF. When OSPF is enbled on at least + one interface return 1. */ +static int ospf_admin_stat(struct ospf *ospf) +{ + struct listnode *node; + struct ospf_interface *oi; + + if (ospf == NULL) + return 0; + + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) + if (oi && oi->address) + return 1; + + return 0; +} + +static uint8_t *ospfGeneralGroup(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct ospf *ospf; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + /* Check whether the instance identifier is valid */ + if (smux_header_generic(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) { + case OSPFROUTERID: /* 1 */ + /* Router-ID of this OSPF instance. */ + if (ospf) + return SNMP_IPADDRESS(ospf->router_id); + else + return SNMP_IPADDRESS(ospf_empty_addr); + case OSPFADMINSTAT: /* 2 */ + /* The administrative status of OSPF in the router. */ + if (ospf_admin_stat(ospf)) + return SNMP_INTEGER(OSPF_STATUS_ENABLED); + else + return SNMP_INTEGER(OSPF_STATUS_DISABLED); + case OSPFVERSIONNUMBER: /* 3 */ + /* OSPF version 2. */ + return SNMP_INTEGER(OSPF_VERSION); + case OSPFAREABDRRTRSTATUS: /* 4 */ + /* Area Border router status. */ + if (ospf && CHECK_FLAG(ospf->flags, OSPF_FLAG_ABR)) + return SNMP_INTEGER(SNMP_TRUE); + else + return SNMP_INTEGER(SNMP_FALSE); + case OSPFASBDRRTRSTATUS: /* 5 */ + /* AS Border router status. */ + if (ospf && CHECK_FLAG(ospf->flags, OSPF_FLAG_ASBR)) + return SNMP_INTEGER(SNMP_TRUE); + else + return SNMP_INTEGER(SNMP_FALSE); + case OSPFEXTERNLSACOUNT: /* 6 */ + /* External LSA counts. */ + if (ospf) + return SNMP_INTEGER(ospf_lsdb_count_all(ospf->lsdb)); + else + return SNMP_INTEGER(0); + case OSPFEXTERNLSACKSUMSUM: /* 7 */ + /* External LSA checksum. */ + return SNMP_INTEGER(0); + case OSPFTOSSUPPORT: /* 8 */ + /* TOS is not supported. */ + return SNMP_INTEGER(SNMP_FALSE); + case OSPFORIGINATENEWLSAS: /* 9 */ + /* The number of new link-state advertisements. */ + if (ospf) + return SNMP_INTEGER(ospf->lsa_originate_count); + else + return SNMP_INTEGER(0); + case OSPFRXNEWLSAS: /* 10 */ + /* The number of link-state advertisements received determined + to be new instantiations. */ + if (ospf) + return SNMP_INTEGER(ospf->rx_lsa_count); + else + return SNMP_INTEGER(0); + case OSPFEXTLSDBLIMIT: /* 11 */ + /* There is no limit for the number of non-default + AS-external-LSAs. */ + return SNMP_INTEGER(-1); + case OSPFMULTICASTEXTENSIONS: /* 12 */ + /* Multicast Extensions to OSPF is not supported. */ + return SNMP_INTEGER(0); + case OSPFEXITOVERFLOWINTERVAL: /* 13 */ + /* Overflow is not supported. */ + return SNMP_INTEGER(0); + case OSPFDEMANDEXTENSIONS: /* 14 */ + /* Demand routing is not supported. */ + return SNMP_INTEGER(SNMP_FALSE); + default: + return NULL; + } + return NULL; +} + +static struct ospf_area * +ospf_area_lookup_next(struct ospf *ospf, struct in_addr *area_id, int first) +{ + struct ospf_area *area; + struct listnode *node; + + if (ospf == NULL) + return NULL; + + if (first) { + node = listhead(ospf->areas); + if (node) { + area = listgetdata(node); + *area_id = area->area_id; + return area; + } + return NULL; + } + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (ntohl(area->area_id.s_addr) > ntohl(area_id->s_addr)) { + *area_id = area->area_id; + return area; + } + } + return NULL; +} + +static struct ospf_area *ospfAreaLookup(struct variable *v, oid name[], + size_t *length, struct in_addr *addr, + int exact) +{ + struct ospf *ospf; + struct ospf_area *area; + int len; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL) + return NULL; + + if (exact) { + /* Length is insufficient to lookup OSPF area. */ + if (*length - v->namelen != sizeof(struct in_addr)) + return NULL; + + oid2in_addr(name + v->namelen, sizeof(struct in_addr), addr); + + area = ospf_area_lookup_by_area_id(ospf, *addr); + + return area; + } else { + len = *length - v->namelen; + if (len > 4) + len = 4; + + oid2in_addr(name + v->namelen, len, addr); + + area = ospf_area_lookup_next(ospf, addr, len == 0 ? 1 : 0); + + if (area == NULL) + return NULL; + + oid_copy_in_addr(name + v->namelen, addr); + *length = sizeof(struct in_addr) + v->namelen; + + return area; + } + return NULL; +} + +static uint8_t *ospfAreaEntry(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct ospf_area *area; + struct in_addr addr; + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + memset(&addr, 0, sizeof(addr)); + + area = ospfAreaLookup(v, name, length, &addr, exact); + if (!area) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) { + case OSPFAREAID: /* 1 */ + return SNMP_IPADDRESS(area->area_id); + case OSPFAUTHTYPE: /* 2 */ + return SNMP_INTEGER(area->auth_type); + case OSPFIMPORTASEXTERN: /* 3 */ + return SNMP_INTEGER(area->external_routing + 1); + case OSPFSPFRUNS: /* 4 */ + return SNMP_INTEGER(area->spf_calculation); + case OSPFAREABDRRTRCOUNT: /* 5 */ + return SNMP_INTEGER(area->abr_count); + case OSPFASBDRRTRCOUNT: /* 6 */ + return SNMP_INTEGER(area->asbr_count); + case OSPFAREALSACOUNT: /* 7 */ + return SNMP_INTEGER(area->lsdb->total); + case OSPFAREALSACKSUMSUM: /* 8 */ + return SNMP_INTEGER(0); + case OSPFAREASUMMARY: /* 9 */ +#define OSPF_noAreaSummary 1 +#define OSPF_sendAreaSummary 2 + if (area->no_summary) + return SNMP_INTEGER(OSPF_noAreaSummary); + else + return SNMP_INTEGER(OSPF_sendAreaSummary); + case OSPFAREASTATUS: /* 10 */ + return SNMP_INTEGER(SNMP_VALID); + default: + return NULL; + } + return NULL; +} + +static struct ospf_area *ospf_stub_area_lookup_next(struct in_addr *area_id, + int first) +{ + struct ospf_area *area; + struct listnode *node; + struct ospf *ospf; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL) + return NULL; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (area->external_routing == OSPF_AREA_STUB) { + if (first) { + *area_id = area->area_id; + return area; + } else if (ntohl(area->area_id.s_addr) + > ntohl(area_id->s_addr)) { + *area_id = area->area_id; + return area; + } + } + } + return NULL; +} + +static struct ospf_area *ospfStubAreaLookup(struct variable *v, oid name[], + size_t *length, + struct in_addr *addr, int exact) +{ + struct ospf *ospf; + struct ospf_area *area; + int len; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL) + return NULL; + + /* Exact lookup. */ + if (exact) { + /* ospfStubAreaID + ospfStubTOS. */ + if (*length != v->namelen + sizeof(struct in_addr) + 1) + return NULL; + + /* Check ospfStubTOS is zero. */ + if (name[*length - 1] != 0) + return NULL; + + oid2in_addr(name + v->namelen, sizeof(struct in_addr), addr); + + area = ospf_area_lookup_by_area_id(ospf, *addr); + + if (area && area->external_routing == OSPF_AREA_STUB) + return area; + else + return NULL; + } else { + len = *length - v->namelen; + if (len > 4) + len = 4; + + oid2in_addr(name + v->namelen, len, addr); + + area = ospf_stub_area_lookup_next(addr, len == 0 ? 1 : 0); + + if (area == NULL) + return NULL; + + oid_copy_in_addr(name + v->namelen, addr); + /* Set TOS 0. */ + name[v->namelen + sizeof(struct in_addr)] = 0; + *length = v->namelen + sizeof(struct in_addr) + 1; + + return area; + } + return NULL; +} + +static uint8_t *ospfStubAreaEntry(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct ospf_area *area; + struct in_addr addr; + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + memset(&addr, 0, sizeof(addr)); + + area = ospfStubAreaLookup(v, name, length, &addr, exact); + if (!area) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) { + case OSPFSTUBAREAID: /* 1 */ + /* OSPF stub area id. */ + return SNMP_IPADDRESS(area->area_id); + case OSPFSTUBTOS: /* 2 */ + /* TOS value is not supported. */ + return SNMP_INTEGER(0); + case OSPFSTUBMETRIC: /* 3 */ + /* Default cost to stub area. */ + return SNMP_INTEGER(area->default_cost); + case OSPFSTUBSTATUS: /* 4 */ + /* Status of the stub area. */ + return SNMP_INTEGER(SNMP_VALID); + case OSPFSTUBMETRICTYPE: /* 5 */ + /* OSPF Metric type. */ +#define OSPF_ospfMetric 1 +#define OSPF_comparableCost 2 +#define OSPF_nonComparable 3 + return SNMP_INTEGER(OSPF_ospfMetric); + default: + return NULL; + } + return NULL; +} + +static struct ospf_lsa *lsdb_lookup_next(struct ospf_area *area, uint8_t *type, + int type_next, struct in_addr *ls_id, + int ls_id_next, + struct in_addr *router_id, + int router_id_next) +{ + struct ospf_lsa *lsa; + int i; + + if (type_next) + i = OSPF_MIN_LSA; + else + i = *type; + + /* Sanity check, if LSA type unknwon + merley skip any LSA */ + if ((i < OSPF_MIN_LSA) || (i >= OSPF_MAX_LSA)) { + zlog_debug("Strange request with LSA type %d", i); + return NULL; + } + + for (; i < OSPF_MAX_LSA; i++) { + *type = i; + + lsa = ospf_lsdb_lookup_by_id_next(area->lsdb, *type, *ls_id, + *router_id, ls_id_next); + if (lsa) + return lsa; + + ls_id_next = 1; + } + return NULL; +} + +static struct ospf_lsa *ospfLsdbLookup(struct variable *v, oid *name, + size_t *length, struct in_addr *area_id, + uint8_t *type, struct in_addr *ls_id, + struct in_addr *router_id, int exact) +{ + struct ospf *ospf; + struct ospf_area *area; + struct ospf_lsa *lsa; + int len; + int type_next; + int ls_id_next; + int router_id_next; + oid *offset; + int offsetlen; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + +#define OSPF_LSDB_ENTRY_OFFSET (IN_ADDR_SIZE + 1 + IN_ADDR_SIZE + IN_ADDR_SIZE) + + if (exact) { + /* Area ID + Type + LS ID + Router ID. */ + if (*length - v->namelen != OSPF_LSDB_ENTRY_OFFSET) + return NULL; + + /* Set OID offset for Area ID. */ + offset = name + v->namelen; + + /* Lookup area first. */ + oid2in_addr(offset, IN_ADDR_SIZE, area_id); + area = ospf_area_lookup_by_area_id(ospf, *area_id); + if (!area) + return NULL; + offset++; + + /* Type. */ + *type = *offset; + offset++; + + /* LS ID. */ + oid2in_addr(offset, IN_ADDR_SIZE, ls_id); + offset++; + + /* Router ID. */ + oid2in_addr(offset, IN_ADDR_SIZE, router_id); + + /* Lookup LSDB. */ + return ospf_lsdb_lookup_by_id(area->lsdb, *type, *ls_id, + *router_id); + } else { + /* Get variable length. */ + offset = name + v->namelen; + offsetlen = *length - v->namelen; + len = offsetlen; + + if (len > (int)IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr(offset, len, area_id); + + /* First we search area. */ + if (len == IN_ADDR_SIZE) + area = ospf_area_lookup_by_area_id(ospf, *area_id); + else + area = ospf_area_lookup_next(ospf, area_id, 1); + + if (area == NULL) + return NULL; + + do { + /* Next we lookup type. */ + offset += len; + offsetlen -= len; + len = offsetlen; + + if (len <= 0) + type_next = 1; + else { + type_next = 0; + *type = *offset; + } + + /* LS ID. */ + offset++; + offsetlen--; + len = offsetlen; + + if (len <= 0) + ls_id_next = 1; + else { + ls_id_next = 0; + if (len > (int)IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr(offset, len, ls_id); + } + + /* Router ID. */ + offset++; + offsetlen -= IN_ADDR_SIZE; + len = offsetlen; + + if (len <= 0) + router_id_next = 1; + else { + router_id_next = 0; + if (len > (int)IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr(offset, len, router_id); + } + + lsa = lsdb_lookup_next(area, type, type_next, ls_id, + ls_id_next, router_id, + router_id_next); + + if (lsa) { + /* Fill in length. */ + *length = v->namelen + OSPF_LSDB_ENTRY_OFFSET; + + /* Fill in value. */ + offset = name + v->namelen; + oid_copy_in_addr(offset, area_id); + offset++; + *offset = lsa->data->type; + offset++; + oid_copy_in_addr(offset, &lsa->data->id); + offset++; + oid_copy_in_addr(offset, + &lsa->data->adv_router); + + return lsa; + } + } while ((area = ospf_area_lookup_next(ospf, area_id, 0)) + != NULL); + } + return NULL; +} + +static uint8_t *ospfLsdbEntry(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct ospf_lsa *lsa; + struct lsa_header *lsah; + struct in_addr area_id; + uint8_t type; + struct in_addr ls_id; + struct in_addr router_id; + struct ospf *ospf; + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + /* INDEX { ospfLsdbAreaId, ospfLsdbType, + ospfLsdbLsid, ospfLsdbRouterId } */ + + memset(&area_id, 0, sizeof(area_id)); + type = 0; + memset(&ls_id, 0, sizeof(ls_id)); + memset(&router_id, 0, sizeof(router_id)); + + /* Check OSPF instance. */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL) + return NULL; + + lsa = ospfLsdbLookup(v, name, length, &area_id, &type, &ls_id, + &router_id, exact); + if (!lsa) + return NULL; + + lsah = lsa->data; + + /* Return the current value of the variable */ + switch (v->magic) { + case OSPFLSDBAREAID: /* 1 */ + return SNMP_IPADDRESS(lsa->area->area_id); + case OSPFLSDBTYPE: /* 2 */ + return SNMP_INTEGER(lsah->type); + case OSPFLSDBLSID: /* 3 */ + return SNMP_IPADDRESS(lsah->id); + case OSPFLSDBROUTERID: /* 4 */ + return SNMP_IPADDRESS(lsah->adv_router); + case OSPFLSDBSEQUENCE: /* 5 */ + return SNMP_INTEGER(lsah->ls_seqnum); + case OSPFLSDBAGE: /* 6 */ + return SNMP_INTEGER(lsah->ls_age); + case OSPFLSDBCHECKSUM: /* 7 */ + return SNMP_INTEGER(lsah->checksum); + case OSPFLSDBADVERTISEMENT: /* 8 */ + *var_len = ntohs(lsah->length); + return (uint8_t *)lsah; + default: + return NULL; + } + return NULL; +} + +static struct ospf_area_range *ospfAreaRangeLookup(struct variable *v, + oid *name, size_t *length, + struct in_addr *area_id, + struct in_addr *range_net, + int exact) +{ + oid *offset; + int offsetlen; + int len; + struct ospf *ospf; + struct ospf_area *area; + struct ospf_area_range *range; + struct prefix_ipv4 p; + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + if (exact) { + /* Area ID + Range Network. */ + if (v->namelen + IN_ADDR_SIZE + IN_ADDR_SIZE != *length) + return NULL; + + /* Set OID offset for Area ID. */ + offset = name + v->namelen; + + /* Lookup area first. */ + oid2in_addr(offset, IN_ADDR_SIZE, area_id); + + area = ospf_area_lookup_by_area_id(ospf, *area_id); + if (!area) + return NULL; + + offset++; + + /* Lookup area range. */ + oid2in_addr(offset, IN_ADDR_SIZE, range_net); + p.prefix = *range_net; + + return ospf_area_range_lookup(area, area->ranges, &p); + } else { + /* Set OID offset for Area ID. */ + offset = name + v->namelen; + offsetlen = *length - v->namelen; + + len = offsetlen; + if (len > (int)IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr(offset, len, area_id); + + /* First we search area. */ + if (len == IN_ADDR_SIZE) + area = ospf_area_lookup_by_area_id(ospf, *area_id); + else + area = ospf_area_lookup_next(ospf, area_id, + len == 0 ? 1 : 0); + + if (area == NULL) + return NULL; + + do { + offset++; + offsetlen -= IN_ADDR_SIZE; + len = offsetlen; + + if (len < 0) + len = 0; + if (len > (int)IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr(offset, len, range_net); + + range = ospf_area_range_lookup_next(area, range_net, + len == 0 ? 1 : 0); + + if (range) { + /* Fill in length. */ + *length = v->namelen + IN_ADDR_SIZE + + IN_ADDR_SIZE; + + /* Fill in value. */ + offset = name + v->namelen; + oid_copy_in_addr(offset, area_id); + offset++; + oid_copy_in_addr(offset, range_net); + + return range; + } + } while ((area = ospf_area_lookup_next(ospf, area_id, 0)) + != NULL); + } + return NULL; +} + +static uint8_t *ospfAreaRangeEntry(struct variable *v, oid *name, + size_t *length, int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct ospf_area_range *range; + struct in_addr area_id; + struct in_addr range_net; + struct in_addr mask; + struct ospf *ospf; + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + /* Check OSPF instance. */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL) + return NULL; + + memset(&area_id, 0, IN_ADDR_SIZE); + memset(&range_net, 0, IN_ADDR_SIZE); + + range = ospfAreaRangeLookup(v, name, length, &area_id, &range_net, + exact); + if (!range) + return NULL; + + /* Convert prefixlen to network mask format. */ + masklen2ip(range->subst_masklen, &mask); + + /* Return the current value of the variable */ + switch (v->magic) { + case OSPFAREARANGEAREAID: /* 1 */ + return SNMP_IPADDRESS(area_id); + case OSPFAREARANGENET: /* 2 */ + return SNMP_IPADDRESS(range_net); + case OSPFAREARANGEMASK: /* 3 */ + return SNMP_IPADDRESS(mask); + case OSPFAREARANGESTATUS: /* 4 */ + return SNMP_INTEGER(SNMP_VALID); + case OSPFAREARANGEEFFECT: /* 5 */ +#define OSPF_advertiseMatching 1 +#define OSPF_doNotAdvertiseMatching 2 + return SNMP_INTEGER(OSPF_advertiseMatching); + default: + return NULL; + } + return NULL; +} + +static struct ospf_nbr_nbma *ospfHostLookup(struct variable *v, oid *name, + size_t *length, + struct in_addr *addr, int exact) +{ + struct ospf_nbr_nbma *nbr_nbma; + struct ospf *ospf; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL) + return NULL; + + if (exact) { + /* INDEX { ospfHostIpAddress, ospfHostTOS } */ + if (*length != v->namelen + IN_ADDR_SIZE + 1) + return NULL; + + /* Check ospfHostTOS. */ + if (name[*length - 1] != 0) + return NULL; + + oid2in_addr(name + v->namelen, IN_ADDR_SIZE, addr); + + nbr_nbma = ospf_nbr_nbma_lookup(ospf, *addr); + + return nbr_nbma; + } + + return NULL; +} + +static uint8_t *ospfHostEntry(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct ospf_nbr_nbma *nbr_nbma; + struct ospf_interface *oi; + struct in_addr addr; + struct ospf *ospf; + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + /* Check OSPF instance. */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL) + return NULL; + + memset(&addr, 0, sizeof(addr)); + + nbr_nbma = ospfHostLookup(v, name, length, &addr, exact); + if (nbr_nbma == NULL) + return NULL; + + oi = nbr_nbma->oi; + + /* Return the current value of the variable */ + switch (v->magic) { + case OSPFHOSTIPADDRESS: /* 1 */ + return SNMP_IPADDRESS(nbr_nbma->addr); + case OSPFHOSTTOS: /* 2 */ + return SNMP_INTEGER(0); + case OSPFHOSTMETRIC: /* 3 */ + if (oi) + return SNMP_INTEGER(oi->output_cost); + else + return SNMP_INTEGER(1); + case OSPFHOSTSTATUS: /* 4 */ + return SNMP_INTEGER(SNMP_VALID); + case OSPFHOSTAREAID: /* 5 */ + if (oi && oi->area) + return SNMP_IPADDRESS(oi->area->area_id); + else + return SNMP_IPADDRESS(ospf_empty_addr); + default: + return NULL; + } + return NULL; +} + +static struct list *ospf_snmp_iflist; + +struct ospf_snmp_if { + struct in_addr addr; + ifindex_t ifindex; + struct interface *ifp; +}; + +static struct ospf_snmp_if *ospf_snmp_if_new(void) +{ + return XCALLOC(MTYPE_SNMP, sizeof(struct ospf_snmp_if)); +} + +static void ospf_snmp_if_free(struct ospf_snmp_if *osif) +{ + XFREE(MTYPE_SNMP, osif); +} + +static int ospf_snmp_if_delete(struct interface *ifp) +{ + struct listnode *node, *nnode; + struct ospf_snmp_if *osif; + + for (ALL_LIST_ELEMENTS(ospf_snmp_iflist, node, nnode, osif)) { + if (osif->ifp == ifp) { + list_delete_node(ospf_snmp_iflist, node); + ospf_snmp_if_free(osif); + break; + } + } + return 0; +} + +static int ospf_snmp_if_update(struct interface *ifp) +{ + struct listnode *node; + struct listnode *pn; + struct connected *ifc; + struct prefix *p; + struct ospf_snmp_if *osif; + struct in_addr *addr; + ifindex_t ifindex; + + ospf_snmp_if_delete(ifp); + + p = NULL; + addr = NULL; + ifindex = 0; + + /* Lookup first IPv4 address entry. */ + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + p = CONNECTED_ID(ifc); + + if (p->family == AF_INET) { + addr = &p->u.prefix4; + break; + } + } + if (!addr) + ifindex = ifp->ifindex; + + /* Add interface to the list. */ + pn = NULL; + for (ALL_LIST_ELEMENTS_RO(ospf_snmp_iflist, node, osif)) { + if (addr) { + /* Usual interfaces --> Sort them based on interface + * IPv4 addresses */ + if (ntohl(osif->addr.s_addr) > ntohl(addr->s_addr)) + break; + } else { + /* Unnumbered interfaces --> Sort them based on + * interface indexes */ + if (osif->addr.s_addr != INADDR_ANY + || osif->ifindex > ifindex) + break; + } + pn = node; + } + + osif = ospf_snmp_if_new(); + if (addr) /* Usual interface */ + { + osif->addr = *addr; + + /* This field is used for storing ospfAddressLessIf OID value, + * conform to RFC1850 OSPF-MIB specification, it must be 0 for + * usual interface */ + osif->ifindex = 0; + } else /* Unnumbered interface */ + osif->ifindex = ifindex; + osif->ifp = ifp; + + listnode_add_after(ospf_snmp_iflist, pn, osif); + return 0; +} + +static int ospf_snmp_is_if_have_addr(struct interface *ifp) +{ + struct listnode *nn; + struct connected *ifc; + + /* Is this interface having any connected IPv4 address ? */ + for (ALL_LIST_ELEMENTS_RO(ifp->connected, nn, ifc)) { + if (CONNECTED_PREFIX(ifc)->family == AF_INET) + return 1; + } + + return 0; +} + +static struct ospf_interface *ospf_snmp_if_lookup(struct in_addr *ifaddr, + ifindex_t *ifindex) +{ + struct listnode *node; + struct ospf_snmp_if *osif; + struct ospf_interface *oi = NULL; + struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + for (ALL_LIST_ELEMENTS_RO(ospf_snmp_iflist, node, osif)) { + if (ifaddr->s_addr) { + if (IPV4_ADDR_SAME(&osif->addr, ifaddr)) + oi = ospf_if_lookup_by_local_addr( + ospf, osif->ifp, *ifaddr); + } else { + if (osif->ifindex == *ifindex) + oi = ospf_if_lookup_by_local_addr( + ospf, osif->ifp, *ifaddr); + } + } + return oi; +} + +static struct ospf_interface *ospf_snmp_if_lookup_next(struct in_addr *ifaddr, + ifindex_t *ifindex, + int ifaddr_next, + ifindex_t ifindex_next) +{ + struct ospf_snmp_if *osif; + struct listnode *nn; + struct ospf *ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + struct ospf_interface *oi = NULL; + + if (ospf == NULL) + return NULL; + + /* No instance is specified --> Return the first OSPF interface */ + if (ifaddr_next) { + for (ALL_LIST_ELEMENTS_RO(ospf_snmp_iflist, nn, osif)) { + osif = listgetdata(nn); + *ifaddr = osif->addr; + *ifindex = osif->ifindex; + /* Because no instance is specified, we don't care about + * the kind of + * interface (usual or unnumbered), just returning the + * first valid + * OSPF interface */ + oi = ospf_if_lookup_by_local_addr(ospf, osif->ifp, + *ifaddr); + if (oi) + return (oi); + } + return NULL; + } + + /* An instance is specified --> Return the next OSPF interface */ + for (ALL_LIST_ELEMENTS_RO(ospf_snmp_iflist, nn, osif)) { + /* Usual interface */ + if (ifaddr->s_addr) { + /* The interface must have valid AF_INET connected + * address */ + /* it must have lager IPv4 address value than the lookup + * entry */ + if ((ospf_snmp_is_if_have_addr(osif->ifp)) + && (ntohl(osif->addr.s_addr) + > ntohl(ifaddr->s_addr))) { + *ifaddr = osif->addr; + *ifindex = osif->ifindex; + + /* and it must be an OSPF interface */ + oi = ospf_if_lookup_by_local_addr( + ospf, osif->ifp, *ifaddr); + if (oi) + return oi; + } + } + /* Unnumbered interface */ + else + /* The interface must NOT have valid AF_INET connected + address */ + /* it must have lager interface index than the lookup + entry */ + if ((!ospf_snmp_is_if_have_addr(osif->ifp)) + && (osif->ifindex > *ifindex)) { + *ifaddr = osif->addr; + *ifindex = osif->ifindex; + + /* and it must be an OSPF interface */ + oi = ospf_if_lookup_by_local_addr(ospf, osif->ifp, + *ifaddr); + if (oi) + return oi; + } + } + return NULL; +} + +static int ospf_snmp_iftype(struct interface *ifp) +{ +#define ospf_snmp_iftype_broadcast 1 +#define ospf_snmp_iftype_nbma 2 +#define ospf_snmp_iftype_pointToPoint 3 +#define ospf_snmp_iftype_pointToMultipoint 5 + if (if_is_broadcast(ifp)) + return ospf_snmp_iftype_broadcast; + if (if_is_pointopoint(ifp)) + return ospf_snmp_iftype_pointToPoint; + return ospf_snmp_iftype_broadcast; +} + +static struct ospf_interface *ospfIfLookup(struct variable *v, oid *name, + size_t *length, + struct in_addr *ifaddr, + ifindex_t *ifindex, int exact) +{ + unsigned int len; + int ifaddr_next = 0; + ifindex_t ifindex_next = 0; + struct ospf_interface *oi; + oid *offset; + + if (exact) { + if (*length != v->namelen + IN_ADDR_SIZE + 1) + return NULL; + + oid2in_addr(name + v->namelen, IN_ADDR_SIZE, ifaddr); + *ifindex = name[v->namelen + IN_ADDR_SIZE]; + + return ospf_snmp_if_lookup(ifaddr, ifindex); + } else { + len = *length - v->namelen; + if (len >= IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + if (len == 0) + ifaddr_next = 1; + + oid2in_addr(name + v->namelen, len, ifaddr); + + len = *length - v->namelen - IN_ADDR_SIZE; + if (len >= 1) + len = 1; + else + ifindex_next = 1; + + if (len == 1) + *ifindex = name[v->namelen + IN_ADDR_SIZE]; + + oi = ospf_snmp_if_lookup_next(ifaddr, ifindex, ifaddr_next, + ifindex_next); + if (oi) { + *length = v->namelen + IN_ADDR_SIZE + 1; + offset = name + v->namelen; + oid_copy_in_addr(offset, ifaddr); + offset++; + *offset = *ifindex; + return oi; + } + } + return NULL; +} + +static uint8_t *ospfIfEntry(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + ifindex_t ifindex; + struct in_addr ifaddr; + struct ospf_interface *oi; + struct ospf *ospf; + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + ifindex = 0; + memset(&ifaddr, 0, sizeof(ifaddr)); + + /* Check OSPF instance. */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL) + return NULL; + + oi = ospfIfLookup(v, name, length, &ifaddr, &ifindex, exact); + if (oi == NULL) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) { + case OSPFIFIPADDRESS: /* 1 */ + return SNMP_IPADDRESS(ifaddr); + case OSPFADDRESSLESSIF: /* 2 */ + return SNMP_INTEGER(ifindex); + case OSPFIFAREAID: /* 3 */ + if (oi->area) + return SNMP_IPADDRESS(oi->area->area_id); + else + return SNMP_IPADDRESS(ospf_empty_addr); + case OSPFIFTYPE: /* 4 */ + return SNMP_INTEGER(ospf_snmp_iftype(oi->ifp)); + case OSPFIFADMINSTAT: /* 5 */ + if (oi) + return SNMP_INTEGER(OSPF_STATUS_ENABLED); + else + return SNMP_INTEGER(OSPF_STATUS_DISABLED); + case OSPFIFRTRPRIORITY: /* 6 */ + return SNMP_INTEGER(PRIORITY(oi)); + case OSPFIFTRANSITDELAY: /* 7 */ + return SNMP_INTEGER(OSPF_IF_PARAM(oi, transmit_delay)); + case OSPFIFRETRANSINTERVAL: /* 8 */ + return SNMP_INTEGER(OSPF_IF_PARAM(oi, retransmit_interval)); + case OSPFIFHELLOINTERVAL: /* 9 */ + return SNMP_INTEGER(OSPF_IF_PARAM(oi, v_hello)); + case OSPFIFRTRDEADINTERVAL: /* 10 */ + return SNMP_INTEGER(OSPF_IF_PARAM(oi, v_wait)); + case OSPFIFPOLLINTERVAL: /* 11 */ + return SNMP_INTEGER(OSPF_POLL_INTERVAL_DEFAULT); + case OSPFIFSTATE: /* 12 */ + return SNMP_INTEGER(ISM_SNMP(oi->state)); + case OSPFIFDESIGNATEDROUTER: /* 13 */ + return SNMP_IPADDRESS(DR(oi)); + case OSPFIFBACKUPDESIGNATEDROUTER: /* 14 */ + return SNMP_IPADDRESS(BDR(oi)); + case OSPFIFEVENTS: /* 15 */ + return SNMP_INTEGER(oi->state_change); + case OSPFIFAUTHKEY: /* 16 */ + *var_len = 0; + return (uint8_t *)OSPF_IF_PARAM(oi, auth_simple); + case OSPFIFSTATUS: /* 17 */ + return SNMP_INTEGER(SNMP_VALID); + case OSPFIFMULTICASTFORWARDING: /* 18 */ +#define ospf_snmp_multiforward_blocked 1 +#define ospf_snmp_multiforward_multicast 2 +#define ospf_snmp_multiforward_unicast 3 + return SNMP_INTEGER(ospf_snmp_multiforward_blocked); + case OSPFIFDEMAND: /* 19 */ + return SNMP_INTEGER(SNMP_FALSE); + case OSPFIFAUTHTYPE: /* 20 */ + if (oi->area) + return SNMP_INTEGER(oi->area->auth_type); + else + return SNMP_INTEGER(0); + default: + return NULL; + } + return NULL; +} + +#define OSPF_SNMP_METRIC_VALUE 1 + +static struct ospf_interface *ospfIfMetricLookup(struct variable *v, oid *name, + size_t *length, + struct in_addr *ifaddr, + ifindex_t *ifindex, int exact) +{ + unsigned int len; + int ifaddr_next = 0; + ifindex_t ifindex_next = 0; + struct ospf_interface *oi; + oid *offset; + int metric; + + if (exact) { + if (*length != v->namelen + IN_ADDR_SIZE + 1 + 1) + return NULL; + + oid2in_addr(name + v->namelen, IN_ADDR_SIZE, ifaddr); + *ifindex = name[v->namelen + IN_ADDR_SIZE]; + metric = name[v->namelen + IN_ADDR_SIZE + 1]; + + if (metric != OSPF_SNMP_METRIC_VALUE) + return NULL; + + return ospf_snmp_if_lookup(ifaddr, ifindex); + } else { + len = *length - v->namelen; + if (len >= IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + else + ifaddr_next = 1; + + oid2in_addr(name + v->namelen, len, ifaddr); + + len = *length - v->namelen - IN_ADDR_SIZE; + if (len >= 1) + len = 1; + else + ifindex_next = 1; + + if (len == 1) + *ifindex = name[v->namelen + IN_ADDR_SIZE]; + + oi = ospf_snmp_if_lookup_next(ifaddr, ifindex, ifaddr_next, + ifindex_next); + if (oi) { + *length = v->namelen + IN_ADDR_SIZE + 1 + 1; + offset = name + v->namelen; + oid_copy_in_addr(offset, ifaddr); + offset++; + *offset = *ifindex; + offset++; + *offset = OSPF_SNMP_METRIC_VALUE; + return oi; + } + } + return NULL; +} + +static uint8_t *ospfIfMetricEntry(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + /* Currently we support metric 1 only. */ + ifindex_t ifindex; + struct in_addr ifaddr; + struct ospf_interface *oi; + struct ospf *ospf; + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + ifindex = 0; + memset(&ifaddr, 0, sizeof(ifaddr)); + + /* Check OSPF instance. */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL) + return NULL; + + oi = ospfIfMetricLookup(v, name, length, &ifaddr, &ifindex, exact); + if (oi == NULL) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) { + case OSPFIFMETRICIPADDRESS: + return SNMP_IPADDRESS(ifaddr); + case OSPFIFMETRICADDRESSLESSIF: + return SNMP_INTEGER(ifindex); + case OSPFIFMETRICTOS: + return SNMP_INTEGER(0); + case OSPFIFMETRICVALUE: + return SNMP_INTEGER(OSPF_SNMP_METRIC_VALUE); + case OSPFIFMETRICSTATUS: + return SNMP_INTEGER(1); + default: + return NULL; + } + return NULL; +} + +static struct route_table *ospf_snmp_vl_table; + +static int ospf_snmp_vl_add(struct ospf_vl_data *vl_data) +{ + struct prefix_ls lp; + struct route_node *rn; + + memset(&lp, 0, sizeof(lp)); + lp.family = AF_UNSPEC; + lp.prefixlen = 64; + lp.id = vl_data->vl_area_id; + lp.adv_router = vl_data->vl_peer; + + rn = route_node_get(ospf_snmp_vl_table, (struct prefix *)&lp); + if (rn->info) + route_unlock_node(rn); + + rn->info = vl_data; + return 0; +} + +static int ospf_snmp_vl_delete(struct ospf_vl_data *vl_data) +{ + struct prefix_ls lp; + struct route_node *rn; + + memset(&lp, 0, sizeof(lp)); + lp.family = AF_UNSPEC; + lp.prefixlen = 64; + lp.id = vl_data->vl_area_id; + lp.adv_router = vl_data->vl_peer; + + rn = route_node_lookup(ospf_snmp_vl_table, (struct prefix *)&lp); + if (!rn) + return 0; + rn->info = NULL; + route_unlock_node(rn); + route_unlock_node(rn); + return 0; +} + +static struct ospf_vl_data *ospf_snmp_vl_lookup(struct in_addr *area_id, + struct in_addr *neighbor) +{ + struct prefix_ls lp; + struct route_node *rn; + struct ospf_vl_data *vl_data; + + memset(&lp, 0, sizeof(lp)); + lp.family = AF_UNSPEC; + lp.prefixlen = 64; + lp.id = *area_id; + lp.adv_router = *neighbor; + + rn = route_node_lookup(ospf_snmp_vl_table, (struct prefix *)&lp); + if (rn) { + vl_data = rn->info; + route_unlock_node(rn); + return vl_data; + } + return NULL; +} + +static struct ospf_vl_data *ospf_snmp_vl_lookup_next(struct in_addr *area_id, + struct in_addr *neighbor, + int first) +{ + struct prefix_ls lp; + struct route_node *rn; + struct ospf_vl_data *vl_data; + + memset(&lp, 0, sizeof(lp)); + lp.family = AF_UNSPEC; + lp.prefixlen = 64; + lp.id = *area_id; + lp.adv_router = *neighbor; + + if (first) + rn = route_top(ospf_snmp_vl_table); + else { + rn = route_node_get(ospf_snmp_vl_table, (struct prefix *)&lp); + rn = route_next(rn); + } + + for (; rn; rn = route_next(rn)) + if (rn->info) + break; + + if (rn && rn->info) { + vl_data = rn->info; + *area_id = vl_data->vl_area_id; + *neighbor = vl_data->vl_peer; + route_unlock_node(rn); + return vl_data; + } + return NULL; +} + +static struct ospf_vl_data * +ospfVirtIfLookup(struct variable *v, oid *name, size_t *length, + struct in_addr *area_id, struct in_addr *neighbor, int exact) +{ + int first; + unsigned int len; + struct ospf_vl_data *vl_data; + + if (exact) { + if (*length != v->namelen + IN_ADDR_SIZE + IN_ADDR_SIZE) + return NULL; + + oid2in_addr(name + v->namelen, IN_ADDR_SIZE, area_id); + oid2in_addr(name + v->namelen + IN_ADDR_SIZE, IN_ADDR_SIZE, + neighbor); + + return ospf_snmp_vl_lookup(area_id, neighbor); + } else { + first = 0; + + len = *length - v->namelen; + if (len == 0) + first = 1; + if (len > IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + oid2in_addr(name + v->namelen, len, area_id); + + len = *length - v->namelen - IN_ADDR_SIZE; + if (len > IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + oid2in_addr(name + v->namelen + IN_ADDR_SIZE, len, neighbor); + + vl_data = ospf_snmp_vl_lookup_next(area_id, neighbor, first); + + if (vl_data) { + *length = v->namelen + IN_ADDR_SIZE + IN_ADDR_SIZE; + oid_copy_in_addr(name + v->namelen, area_id); + oid_copy_in_addr(name + v->namelen + IN_ADDR_SIZE, + neighbor); + return vl_data; + } + } + return NULL; +} + +static uint8_t *ospfVirtIfEntry(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct ospf_vl_data *vl_data; + struct ospf_interface *oi; + struct in_addr area_id; + struct in_addr neighbor; + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + memset(&area_id, 0, sizeof(area_id)); + memset(&neighbor, 0, sizeof(neighbor)); + + vl_data = ospfVirtIfLookup(v, name, length, &area_id, &neighbor, exact); + if (!vl_data) + return NULL; + oi = vl_data->vl_oi; + if (!oi) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) { + case OSPFVIRTIFAREAID: + return SNMP_IPADDRESS(area_id); + case OSPFVIRTIFNEIGHBOR: + return SNMP_IPADDRESS(neighbor); + case OSPFVIRTIFTRANSITDELAY: + return SNMP_INTEGER(OSPF_IF_PARAM(oi, transmit_delay)); + case OSPFVIRTIFRETRANSINTERVAL: + return SNMP_INTEGER(OSPF_IF_PARAM(oi, retransmit_interval)); + case OSPFVIRTIFHELLOINTERVAL: + return SNMP_INTEGER(OSPF_IF_PARAM(oi, v_hello)); + case OSPFVIRTIFRTRDEADINTERVAL: + return SNMP_INTEGER(OSPF_IF_PARAM(oi, v_wait)); + case OSPFVIRTIFSTATE: + return SNMP_INTEGER(oi->state); + case OSPFVIRTIFEVENTS: + return SNMP_INTEGER(oi->state_change); + case OSPFVIRTIFAUTHKEY: + *var_len = 0; + return (uint8_t *)OSPF_IF_PARAM(oi, auth_simple); + case OSPFVIRTIFSTATUS: + return SNMP_INTEGER(SNMP_VALID); + case OSPFVIRTIFAUTHTYPE: + if (oi->area) + return SNMP_INTEGER(oi->area->auth_type); + else + return SNMP_INTEGER(0); + default: + return NULL; + } + return NULL; +} + +static struct ospf_neighbor *ospf_snmp_nbr_lookup(struct ospf *ospf, + struct in_addr *nbr_addr, + ifindex_t *ifindex) +{ + struct listnode *node, *nnode; + struct ospf_interface *oi; + struct ospf_neighbor *nbr; + struct route_node *rn; + + for (ALL_LIST_ELEMENTS(ospf->oiflist, node, nnode, oi)) { + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) + if ((nbr = rn->info) != NULL + && nbr != oi->nbr_self + /* If EXACT match is needed, provide ALL entry found + && nbr->state != NSM_Down + */ + && nbr->src.s_addr != INADDR_ANY) { + if (IPV4_ADDR_SAME(&nbr->src, nbr_addr)) { + route_unlock_node(rn); + return nbr; + } + } + } + return NULL; +} + +static struct ospf_neighbor *ospf_snmp_nbr_lookup_next(struct in_addr *nbr_addr, + ifindex_t *ifindex, + int first) +{ + struct listnode *nn; + struct ospf_interface *oi; + struct ospf_neighbor *nbr; + struct route_node *rn; + struct ospf_neighbor *min = NULL; + struct ospf *ospf; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, nn, oi)) { + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) + if ((nbr = rn->info) != NULL && nbr != oi->nbr_self + && nbr->state != NSM_Down + && nbr->src.s_addr != INADDR_ANY) { + if (first) { + if (!min) + min = nbr; + else if (ntohl(nbr->src.s_addr) + < ntohl(min->src.s_addr)) + min = nbr; + } else if (ntohl(nbr->src.s_addr) + > ntohl(nbr_addr->s_addr)) { + if (!min) + min = nbr; + else if (ntohl(nbr->src.s_addr) + < ntohl(min->src.s_addr)) + min = nbr; + } + } + } + if (min) { + *nbr_addr = min->src; + *ifindex = 0; + return min; + } + return NULL; +} + +static struct ospf_neighbor *ospfNbrLookup(struct variable *v, oid *name, + size_t *length, + struct in_addr *nbr_addr, + ifindex_t *ifindex, int exact) +{ + unsigned int len; + int first; + struct ospf_neighbor *nbr; + struct ospf *ospf; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + if (!ospf) + return NULL; + + if (exact) { + if (*length != v->namelen + IN_ADDR_SIZE + 1) + return NULL; + + oid2in_addr(name + v->namelen, IN_ADDR_SIZE, nbr_addr); + *ifindex = name[v->namelen + IN_ADDR_SIZE]; + + return ospf_snmp_nbr_lookup(ospf, nbr_addr, ifindex); + } else { + first = 0; + len = *length - v->namelen; + + if (len == 0) + first = 1; + + if (len > IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr(name + v->namelen, len, nbr_addr); + + len = *length - v->namelen - IN_ADDR_SIZE; + if (len >= 1) + *ifindex = name[v->namelen + IN_ADDR_SIZE]; + + nbr = ospf_snmp_nbr_lookup_next(nbr_addr, ifindex, first); + + if (nbr) { + *length = v->namelen + IN_ADDR_SIZE + 1; + oid_copy_in_addr(name + v->namelen, nbr_addr); + name[v->namelen + IN_ADDR_SIZE] = *ifindex; + return nbr; + } + } + return NULL; +} + +/* map internal frr neighbor states to official MIB values: + +ospfNbrState OBJECT-TYPE + SYNTAX INTEGER { + down (1), + attempt (2), + init (3), + twoWay (4), + exchangeStart (5), + exchange (6), + loading (7), + full (8) + } +*/ +static int32_t ospf_snmp_neighbor_state(uint8_t nst) +{ + switch (nst) { + case NSM_Attempt: + return 2; + case NSM_Init: + return 3; + case NSM_TwoWay: + return 4; + case NSM_ExStart: + return 5; + case NSM_Exchange: + return 6; + case NSM_Loading: + return 7; + case NSM_Full: + return 8; + default: + return 1; /* down */ + } +} + +static uint8_t *ospfNbrEntry(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct in_addr nbr_addr; + ifindex_t ifindex; + struct ospf_neighbor *nbr; + struct ospf_interface *oi; + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + memset(&nbr_addr, 0, sizeof(nbr_addr)); + ifindex = 0; + + nbr = ospfNbrLookup(v, name, length, &nbr_addr, &ifindex, exact); + if (!nbr) + return NULL; + oi = nbr->oi; + + /* Return the current value of the variable */ + switch (v->magic) { + case OSPFNBRIPADDR: + return SNMP_IPADDRESS(nbr_addr); + case OSPFNBRADDRESSLESSINDEX: + return SNMP_INTEGER(ifindex); + case OSPFNBRRTRID: + return SNMP_IPADDRESS(nbr->router_id); + case OSPFNBROPTIONS: + return SNMP_INTEGER(oi->nbr_self->options); + case OSPFNBRPRIORITY: + return SNMP_INTEGER(nbr->priority); + case OSPFNBRSTATE: + return SNMP_INTEGER(ospf_snmp_neighbor_state(nbr->state)); + case OSPFNBREVENTS: + return SNMP_INTEGER(nbr->state_change); + case OSPFNBRLSRETRANSQLEN: + return SNMP_INTEGER(ospf_ls_retransmit_count(nbr)); + case OSPFNBMANBRSTATUS: + return SNMP_INTEGER(SNMP_VALID); + case OSPFNBMANBRPERMANENCE: + return SNMP_INTEGER(2); + case OSPFNBRHELLOSUPPRESSED: + return SNMP_INTEGER(SNMP_FALSE); + default: + return NULL; + } + return NULL; +} + +static uint8_t *ospfVirtNbrEntry(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct ospf_vl_data *vl_data; + struct in_addr area_id; + struct in_addr neighbor; + struct ospf *ospf; + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + memset(&area_id, 0, sizeof(area_id)); + memset(&neighbor, 0, sizeof(neighbor)); + + /* Check OSPF instance. */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL) + return NULL; + + vl_data = ospfVirtIfLookup(v, name, length, &area_id, &neighbor, exact); + if (!vl_data) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) { + case OSPFVIRTNBRAREA: + return (uint8_t *)NULL; + case OSPFVIRTNBRRTRID: + return (uint8_t *)NULL; + case OSPFVIRTNBRIPADDR: + return (uint8_t *)NULL; + case OSPFVIRTNBROPTIONS: + return (uint8_t *)NULL; + case OSPFVIRTNBRSTATE: + return (uint8_t *)NULL; + case OSPFVIRTNBREVENTS: + return (uint8_t *)NULL; + case OSPFVIRTNBRLSRETRANSQLEN: + return (uint8_t *)NULL; + case OSPFVIRTNBRHELLOSUPPRESSED: + return (uint8_t *)NULL; + default: + return NULL; + } + return NULL; +} + +static struct ospf_lsa *ospfExtLsdbLookup(struct variable *v, oid *name, + size_t *length, uint8_t *type, + struct in_addr *ls_id, + struct in_addr *router_id, int exact) +{ + int first; + oid *offset; + int offsetlen; + uint8_t lsa_type; + unsigned int len; + struct ospf_lsa *lsa; + struct ospf *ospf; + + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (exact) { + if (*length != v->namelen + 1 + IN_ADDR_SIZE + IN_ADDR_SIZE) + return NULL; + + offset = name + v->namelen; + + /* Make it sure given value match to type. */ + lsa_type = *offset; + offset++; + + if (lsa_type != *type) + return NULL; + + /* LS ID. */ + oid2in_addr(offset, IN_ADDR_SIZE, ls_id); + offset++; + + /* Router ID. */ + oid2in_addr(offset, IN_ADDR_SIZE, router_id); + + return ospf_lsdb_lookup_by_id(ospf->lsdb, *type, *ls_id, + *router_id); + } else { + /* Get variable length. */ + first = 0; + offset = name + v->namelen; + offsetlen = *length - v->namelen; + + /* LSA type value. */ + lsa_type = *offset; + offset++; + offsetlen--; + + if (offsetlen <= 0 || lsa_type < OSPF_AS_EXTERNAL_LSA) + first = 1; + + /* LS ID. */ + len = offsetlen; + if (len > IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr(offset, len, ls_id); + + offset++; + offsetlen -= IN_ADDR_SIZE; + + /* Router ID. */ + len = offsetlen; + if (len > IN_ADDR_SIZE) + len = IN_ADDR_SIZE; + + oid2in_addr(offset, len, router_id); + + lsa = ospf_lsdb_lookup_by_id_next(ospf->lsdb, *type, *ls_id, + *router_id, first); + + if (lsa) { + /* Fill in length. */ + *length = v->namelen + 1 + IN_ADDR_SIZE + IN_ADDR_SIZE; + + /* Fill in value. */ + offset = name + v->namelen; + + *offset = OSPF_AS_EXTERNAL_LSA; + offset++; + oid_copy_in_addr(offset, &lsa->data->id); + offset++; + oid_copy_in_addr(offset, &lsa->data->adv_router); + + return lsa; + } + } + return NULL; +} + +static uint8_t *ospfExtLsdbEntry(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct ospf_lsa *lsa; + struct lsa_header *lsah; + uint8_t type; + struct in_addr ls_id; + struct in_addr router_id; + struct ospf *ospf; + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + type = OSPF_AS_EXTERNAL_LSA; + memset(&ls_id, 0, sizeof(ls_id)); + memset(&router_id, 0, sizeof(router_id)); + + /* Check OSPF instance. */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL) + return NULL; + + lsa = ospfExtLsdbLookup(v, name, length, &type, &ls_id, &router_id, + exact); + if (!lsa) + return NULL; + + lsah = lsa->data; + + /* Return the current value of the variable */ + switch (v->magic) { + case OSPFEXTLSDBTYPE: + return SNMP_INTEGER(OSPF_AS_EXTERNAL_LSA); + case OSPFEXTLSDBLSID: + return SNMP_IPADDRESS(lsah->id); + case OSPFEXTLSDBROUTERID: + return SNMP_IPADDRESS(lsah->adv_router); + case OSPFEXTLSDBSEQUENCE: + return SNMP_INTEGER(lsah->ls_seqnum); + case OSPFEXTLSDBAGE: + return SNMP_INTEGER(lsah->ls_age); + case OSPFEXTLSDBCHECKSUM: + return SNMP_INTEGER(lsah->checksum); + case OSPFEXTLSDBADVERTISEMENT: + *var_len = ntohs(lsah->length); + return (uint8_t *)lsah; + default: + return NULL; + } + return NULL; +} + +static uint8_t *ospfAreaAggregateEntry(struct variable *v, oid *name, + size_t *length, int exact, + size_t *var_len, + WriteMethod **write_method) +{ + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) { + case OSPFAREAAGGREGATEAREAID: + return (uint8_t *)NULL; + case OSPFAREAAGGREGATELSDBTYPE: + return (uint8_t *)NULL; + case OSPFAREAAGGREGATENET: + return (uint8_t *)NULL; + case OSPFAREAAGGREGATEMASK: + return (uint8_t *)NULL; + case OSPFAREAAGGREGATESTATUS: + return (uint8_t *)NULL; + case OSPFAREAAGGREGATEEFFECT: + return (uint8_t *)NULL; + default: + return NULL; + } + return NULL; +} + +/* OSPF Traps. */ +#define IFSTATECHANGE 16 +#define VIRTIFSTATECHANGE 1 +#define NBRSTATECHANGE 2 +#define VIRTNBRSTATECHANGE 3 + +static struct trap_object ospfNbrTrapList[] = {{-2, {1, OSPFROUTERID}}, + {3, {10, 1, OSPFNBRIPADDR}}, + {3, {10, 1, OSPFNBRRTRID}}, + {3, {10, 1, OSPFNBRSTATE}}}; + + +static struct trap_object ospfVirtNbrTrapList[] = { + {-2, {1, 1}}, + {3, {11, 1, OSPFVIRTNBRAREA}}, + {3, {11, 1, OSPFVIRTNBRRTRID}}, + {3, {11, 1, OSPFVIRTNBRSTATE}}}; + +static struct trap_object ospfIfTrapList[] = {{-2, {1, OSPFROUTERID}}, + {3, {7, 1, OSPFIFIPADDRESS}}, + {3, {7, 1, OSPFADDRESSLESSIF}}, + {3, {7, 1, OSPFIFSTATE}}}; + +static struct trap_object ospfVirtIfTrapList[] = { + {-2, {1, OSPFROUTERID}}, + {3, {9, 1, OSPFVIRTIFAREAID}}, + {3, {9, 1, OSPFVIRTIFNEIGHBOR}}, + {3, {9, 1, OSPFVIRTIFSTATE}}}; + +static void ospfTrapNbrStateChange(struct ospf_neighbor *on) +{ + oid index[sizeof(oid) * (IN_ADDR_SIZE + 1)]; + char msgbuf[16]; + + ospf_nbr_ism_state_message(on, msgbuf, sizeof(msgbuf)); + if (IS_DEBUG_OSPF_EVENT) + zlog_info("%s: trap sent: %pI4 now %s", __func__, + &on->address.u.prefix4, msgbuf); + + oid_copy_in_addr(index, &(on->address.u.prefix4)); + index[IN_ADDR_SIZE] = 0; + + smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid, + array_size(ospf_trap_oid), ospf_oid, + sizeof(ospf_oid) / sizeof(oid), index, IN_ADDR_SIZE + 1, + ospfNbrTrapList, array_size(ospfNbrTrapList), NBRSTATECHANGE); +} + +static void ospfTrapVirtNbrStateChange(struct ospf_neighbor *on) +{ + oid index[sizeof(oid) * (IN_ADDR_SIZE + 1)]; + + zlog_info("ospfTrapVirtNbrStateChange trap sent"); + + oid_copy_in_addr(index, &(on->address.u.prefix4)); + index[IN_ADDR_SIZE] = 0; + + smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid, + array_size(ospf_trap_oid), ospf_oid, + sizeof(ospf_oid) / sizeof(oid), index, IN_ADDR_SIZE + 1, + ospfVirtNbrTrapList, array_size(ospfVirtNbrTrapList), + VIRTNBRSTATECHANGE); +} + +static int ospf_snmp_nsm_change(struct ospf_neighbor *nbr, int next_state, + int old_state) +{ + /* Transition to/from state Full should be handled only by + * DR when in Broadcast or Non-Brodcast Multi-Access networks + */ + if ((next_state == NSM_Full || old_state == NSM_Full) + && (nbr->oi->state != ISM_DR) + && (nbr->oi->type == OSPF_IFTYPE_BROADCAST + || nbr->oi->type == OSPF_IFTYPE_NBMA)) + return 0; + + /* State progression to non-terminal state */ + if (next_state > old_state && next_state != NSM_Full + && next_state != NSM_TwoWay) + return 0; + + if (nbr->oi->type == OSPF_IFTYPE_VIRTUALLINK) + ospfTrapVirtNbrStateChange(nbr); + else + ospfTrapNbrStateChange(nbr); + + return 0; +} + +static void ospfTrapIfStateChange(struct ospf_interface *oi) +{ + oid index[sizeof(oid) * (IN_ADDR_SIZE + 1)]; + + if (IS_DEBUG_OSPF_EVENT) + zlog_info("%s: trap sent: %pI4 now %s", __func__, + &oi->address->u.prefix4, + lookup_msg(ospf_ism_state_msg, oi->state, NULL)); + + oid_copy_in_addr(index, &(oi->address->u.prefix4)); + index[IN_ADDR_SIZE] = 0; + + smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid, + array_size(ospf_trap_oid), ospf_oid, + sizeof(ospf_oid) / sizeof(oid), index, IN_ADDR_SIZE + 1, + ospfIfTrapList, array_size(ospfIfTrapList), IFSTATECHANGE); +} + +static void ospfTrapVirtIfStateChange(struct ospf_interface *oi) +{ + oid index[sizeof(oid) * (IN_ADDR_SIZE + 1)]; + + zlog_info("ospfTrapVirtIfStateChange trap sent"); + + oid_copy_in_addr(index, &(oi->address->u.prefix4)); + index[IN_ADDR_SIZE] = 0; + + smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid, + array_size(ospf_trap_oid), ospf_oid, + sizeof(ospf_oid) / sizeof(oid), index, IN_ADDR_SIZE + 1, + ospfVirtIfTrapList, array_size(ospfVirtIfTrapList), + VIRTIFSTATECHANGE); +} + +static int ospf_snmp_ism_change(struct ospf_interface *oi, int state, + int old_state) +{ + /* Terminal state or regression */ + if ((state == ISM_DR) || (state == ISM_Backup) || (state == ISM_DROther) + || (state == ISM_PointToPoint) || (state < old_state)) { + /* ospfVirtIfStateChange */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + ospfTrapVirtIfStateChange(oi); + /* ospfIfStateChange */ + else + ospfTrapIfStateChange(oi); + } + return 0; +} + +/* Register OSPF2-MIB. */ +static int ospf_snmp_init(struct event_loop *tm) +{ + ospf_snmp_iflist = list_new(); + ospf_snmp_vl_table = route_table_init(); + smux_init(tm); + REGISTER_MIB("mibII/ospf", ospf_variables, variable, ospf_oid); + return 0; +} + +static int ospf_snmp_module_init(void) +{ + hook_register(ospf_if_update, ospf_snmp_if_update); + hook_register(ospf_if_delete, ospf_snmp_if_delete); + hook_register(ospf_vl_add, ospf_snmp_vl_add); + hook_register(ospf_vl_delete, ospf_snmp_vl_delete); + hook_register(ospf_ism_change, ospf_snmp_ism_change); + hook_register(ospf_nsm_change, ospf_snmp_nsm_change); + + hook_register(frr_late_init, ospf_snmp_init); + return 0; +} + +FRR_MODULE_SETUP(.name = "ospfd_snmp", .version = FRR_VERSION, + .description = "ospfd AgentX SNMP module", + .init = ospf_snmp_module_init, +); diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c new file mode 100644 index 0000000..274d5bc --- /dev/null +++ b/ospfd/ospf_spf.c @@ -0,0 +1,2073 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* OSPF SPF calculation. + * Copyright (C) 1999, 2000 Kunihiro Ishiguro, Toshiaki Takada + */ + +#include + +#include "monotime.h" +#include "frrevent.h" +#include "memory.h" +#include "hash.h" +#include "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "log.h" +#include "sockunion.h" /* for inet_ntop () */ + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ia.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_sr.h" +#include "ospfd/ospf_ti_lfa.h" +#include "ospfd/ospf_errors.h" + +#ifdef SUPPORT_OSPF_API +#include "ospfd/ospf_apiserver.h" +#endif + +/* Variables to ensure a SPF scheduled log message is printed only once */ + +static unsigned int spf_reason_flags = 0; + +/* dummy vertex to flag "in spftree" */ +static const struct vertex vertex_in_spftree = {}; +#define LSA_SPF_IN_SPFTREE (struct vertex *)&vertex_in_spftree +#define LSA_SPF_NOT_EXPLORED NULL + +static void ospf_clear_spf_reason_flags(void) +{ + spf_reason_flags = 0; +} + +static void ospf_spf_set_reason(ospf_spf_reason_t reason) +{ + spf_reason_flags |= 1 << reason; +} + +static void ospf_vertex_free(void *); + +/* + * Heap related functions, for the managment of the candidates, to + * be used with pqueue. + */ +static int vertex_cmp(const struct vertex *v1, const struct vertex *v2) +{ + if (v1->distance != v2->distance) + return v1->distance - v2->distance; + + if (v1->type != v2->type) { + switch (v1->type) { + case OSPF_VERTEX_NETWORK: + return -1; + case OSPF_VERTEX_ROUTER: + return 1; + } + } + return 0; +} +DECLARE_SKIPLIST_NONUNIQ(vertex_pqueue, struct vertex, pqi, vertex_cmp); + +static void lsdb_clean_stat(struct ospf_lsdb *lsdb) +{ + struct route_table *table; + struct route_node *rn; + struct ospf_lsa *lsa; + int i; + + for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) { + table = lsdb->type[i].db; + for (rn = route_top(table); rn; rn = route_next(rn)) + if ((lsa = (rn->info)) != NULL) + lsa->stat = LSA_SPF_NOT_EXPLORED; + } +} + +static struct vertex_nexthop *vertex_nexthop_new(void) +{ + return XCALLOC(MTYPE_OSPF_NEXTHOP, sizeof(struct vertex_nexthop)); +} + +static void vertex_nexthop_free(struct vertex_nexthop *nh) +{ + XFREE(MTYPE_OSPF_NEXTHOP, nh); +} + +/* + * Free the canonical nexthop objects for an area, ie the nexthop objects + * attached to the first-hop router vertices, and any intervening network + * vertices. + */ +static void ospf_canonical_nexthops_free(struct vertex *root) +{ + struct listnode *node, *nnode; + struct vertex *child; + + for (ALL_LIST_ELEMENTS(root->children, node, nnode, child)) { + struct listnode *n2, *nn2; + struct vertex_parent *vp; + + /* + * router vertices through an attached network each + * have a distinct (canonical / not inherited) nexthop + * which must be freed. + * + * A network vertex can only have router vertices as its + * children, so only one level of recursion is possible. + */ + if (child->type == OSPF_VERTEX_NETWORK) + ospf_canonical_nexthops_free(child); + + /* Free child nexthops pointing back to this root vertex */ + for (ALL_LIST_ELEMENTS(child->parents, n2, nn2, vp)) { + if (vp->parent == root && vp->nexthop) { + vertex_nexthop_free(vp->nexthop); + vp->nexthop = NULL; + if (vp->local_nexthop) { + vertex_nexthop_free(vp->local_nexthop); + vp->local_nexthop = NULL; + } + } + } + } +} + +/* + * TODO: Parent list should be excised, in favour of maintaining only + * vertex_nexthop, with refcounts. + */ +static struct vertex_parent *vertex_parent_new(struct vertex *v, int backlink, + struct vertex_nexthop *hop, + struct vertex_nexthop *lhop) +{ + struct vertex_parent *new; + + new = XMALLOC(MTYPE_OSPF_VERTEX_PARENT, sizeof(struct vertex_parent)); + + new->parent = v; + new->backlink = backlink; + new->nexthop = hop; + new->local_nexthop = lhop; + + return new; +} + +static void vertex_parent_free(struct vertex_parent *p) +{ + vertex_nexthop_free(p->local_nexthop); + vertex_nexthop_free(p->nexthop); + XFREE(MTYPE_OSPF_VERTEX_PARENT, p); +} + +int vertex_parent_cmp(void *aa, void *bb) +{ + struct vertex_parent *a = aa, *b = bb; + return IPV4_ADDR_CMP(&a->nexthop->router, &b->nexthop->router); +} + +static struct vertex *ospf_vertex_new(struct ospf_area *area, + struct ospf_lsa *lsa) +{ + struct vertex *new; + + new = XCALLOC(MTYPE_OSPF_VERTEX, sizeof(struct vertex)); + + new->flags = 0; + new->type = lsa->data->type; + new->id = lsa->data->id; + new->lsa = lsa->data; + new->children = list_new(); + new->parents = list_new(); + new->parents->del = (void (*)(void *))vertex_parent_free; + new->parents->cmp = vertex_parent_cmp; + new->lsa_p = lsa; + + lsa->stat = new; + + listnode_add(area->spf_vertex_list, new); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Created %s vertex %pI4", __func__, + new->type == OSPF_VERTEX_ROUTER ? "Router" + : "Network", + &new->lsa->id); + + return new; +} + +static void ospf_vertex_free(void *data) +{ + struct vertex *v = data; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Free %s vertex %pI4", __func__, + v->type == OSPF_VERTEX_ROUTER ? "Router" : "Network", + &v->lsa->id); + + if (v->children) + list_delete(&v->children); + + if (v->parents) + list_delete(&v->parents); + + v->lsa = NULL; + + XFREE(MTYPE_OSPF_VERTEX, v); +} + +static void ospf_vertex_dump(const char *msg, struct vertex *v, + int print_parents, int print_children) +{ + if (!IS_DEBUG_OSPF_EVENT) + return; + + zlog_debug("%s %s vertex %pI4 distance %u flags %u", msg, + v->type == OSPF_VERTEX_ROUTER ? "Router" : "Network", + &v->lsa->id, v->distance, (unsigned int)v->flags); + + if (print_parents) { + struct listnode *node; + struct vertex_parent *vp; + + for (ALL_LIST_ELEMENTS_RO(v->parents, node, vp)) { + if (vp) { + zlog_debug( + "parent %pI4 backlink %d nexthop %pI4 lsa pos %d", + &vp->parent->lsa->id, vp->backlink, + &vp->nexthop->router, + vp->nexthop->lsa_pos); + } + } + } + + if (print_children) { + struct listnode *cnode; + struct vertex *cv; + + for (ALL_LIST_ELEMENTS_RO(v->children, cnode, cv)) + ospf_vertex_dump(" child:", cv, 0, 0); + } +} + + +/* Add a vertex to the list of children in each of its parents. */ +static void ospf_vertex_add_parent(struct vertex *v) +{ + struct vertex_parent *vp; + struct listnode *node; + + assert(v && v->parents); + + for (ALL_LIST_ELEMENTS_RO(v->parents, node, vp)) { + assert(vp->parent && vp->parent->children); + + /* No need to add two links from the same parent. */ + if (listnode_lookup(vp->parent->children, v) == NULL) + listnode_add(vp->parent->children, v); + } +} + +/* Find a vertex according to its router id */ +struct vertex *ospf_spf_vertex_find(struct in_addr id, struct list *vertex_list) +{ + struct listnode *node; + struct vertex *found; + + for (ALL_LIST_ELEMENTS_RO(vertex_list, node, found)) { + if (found->id.s_addr == id.s_addr) + return found; + } + + return NULL; +} + +/* Find a vertex parent according to its router id */ +struct vertex_parent *ospf_spf_vertex_parent_find(struct in_addr id, + struct vertex *vertex) +{ + struct listnode *node; + struct vertex_parent *found; + + for (ALL_LIST_ELEMENTS_RO(vertex->parents, node, found)) { + if (found->parent->id.s_addr == id.s_addr) + return found; + } + + return NULL; +} + +struct vertex *ospf_spf_vertex_by_nexthop(struct vertex *root, + struct in_addr *nexthop) +{ + struct listnode *node; + struct vertex *child; + struct vertex_parent *vertex_parent; + + for (ALL_LIST_ELEMENTS_RO(root->children, node, child)) { + vertex_parent = ospf_spf_vertex_parent_find(root->id, child); + if (vertex_parent->nexthop->router.s_addr == nexthop->s_addr) + return child; + } + + return NULL; +} + +/* Create a deep copy of a SPF vertex without children and parents */ +static struct vertex *ospf_spf_vertex_copy(struct vertex *vertex) +{ + struct vertex *copy; + + copy = XCALLOC(MTYPE_OSPF_VERTEX, sizeof(struct vertex)); + + memcpy(copy, vertex, sizeof(struct vertex)); + copy->parents = list_new(); + copy->parents->del = (void (*)(void *))vertex_parent_free; + copy->parents->cmp = vertex_parent_cmp; + copy->children = list_new(); + + return copy; +} + +/* Create a deep copy of a SPF vertex_parent */ +static struct vertex_parent * +ospf_spf_vertex_parent_copy(struct vertex_parent *vertex_parent) +{ + struct vertex_parent *vertex_parent_copy; + struct vertex_nexthop *nexthop_copy, *local_nexthop_copy; + + vertex_parent_copy = + XCALLOC(MTYPE_OSPF_VERTEX, sizeof(struct vertex_parent)); + + nexthop_copy = vertex_nexthop_new(); + local_nexthop_copy = vertex_nexthop_new(); + + memcpy(vertex_parent_copy, vertex_parent, sizeof(struct vertex_parent)); + memcpy(nexthop_copy, vertex_parent->nexthop, + sizeof(struct vertex_nexthop)); + memcpy(local_nexthop_copy, vertex_parent->local_nexthop, + sizeof(struct vertex_nexthop)); + + vertex_parent_copy->nexthop = nexthop_copy; + vertex_parent_copy->local_nexthop = local_nexthop_copy; + + return vertex_parent_copy; +} + +/* Create a deep copy of a SPF tree */ +void ospf_spf_copy(struct vertex *vertex, struct list *vertex_list) +{ + struct listnode *node; + struct vertex *vertex_copy, *child, *child_copy, *parent_copy; + struct vertex_parent *vertex_parent, *vertex_parent_copy; + + /* First check if the node is already in the vertex list */ + vertex_copy = ospf_spf_vertex_find(vertex->id, vertex_list); + if (!vertex_copy) { + vertex_copy = ospf_spf_vertex_copy(vertex); + listnode_add(vertex_list, vertex_copy); + } + + /* Copy all parents, create parent nodes if necessary */ + for (ALL_LIST_ELEMENTS_RO(vertex->parents, node, vertex_parent)) { + parent_copy = ospf_spf_vertex_find(vertex_parent->parent->id, + vertex_list); + if (!parent_copy) { + parent_copy = + ospf_spf_vertex_copy(vertex_parent->parent); + listnode_add(vertex_list, parent_copy); + } + vertex_parent_copy = ospf_spf_vertex_parent_copy(vertex_parent); + vertex_parent_copy->parent = parent_copy; + listnode_add(vertex_copy->parents, vertex_parent_copy); + } + + /* Copy all children, create child nodes if necessary */ + for (ALL_LIST_ELEMENTS_RO(vertex->children, node, child)) { + child_copy = ospf_spf_vertex_find(child->id, vertex_list); + if (!child_copy) { + child_copy = ospf_spf_vertex_copy(child); + listnode_add(vertex_list, child_copy); + } + listnode_add(vertex_copy->children, child_copy); + } + + /* Finally continue copying with child nodes */ + for (ALL_LIST_ELEMENTS_RO(vertex->children, node, child)) + ospf_spf_copy(child, vertex_list); +} + +static void ospf_spf_remove_branch(struct vertex_parent *vertex_parent, + struct vertex *child, + struct list *vertex_list) +{ + struct listnode *node, *nnode, *inner_node, *inner_nnode; + struct vertex *grandchild; + struct vertex_parent *vertex_parent_found; + bool has_more_links = false; + + /* + * First check if there are more nexthops for that parent to that child + */ + for (ALL_LIST_ELEMENTS_RO(child->parents, node, vertex_parent_found)) { + if (vertex_parent_found->parent->id.s_addr + == vertex_parent->parent->id.s_addr + && vertex_parent_found->nexthop->router.s_addr + != vertex_parent->nexthop->router.s_addr) + has_more_links = true; + } + + /* + * No more links from that parent? Then delete the child from its + * children list. + */ + if (!has_more_links) + listnode_delete(vertex_parent->parent->children, child); + + /* + * Delete the vertex_parent from the child parents list, this needs to + * be done anyway. + */ + listnode_delete(child->parents, vertex_parent); + + /* + * Are there actually more parents left? If not, then delete the child! + * This is done by recursively removing the links to the grandchildren, + * such that finally the child can be removed without leaving unused + * partial branches. + */ + if (child->parents->count == 0) { + for (ALL_LIST_ELEMENTS(child->children, node, nnode, + grandchild)) { + for (ALL_LIST_ELEMENTS(grandchild->parents, inner_node, + inner_nnode, + vertex_parent_found)) { + ospf_spf_remove_branch(vertex_parent_found, + grandchild, vertex_list); + } + } + listnode_delete(vertex_list, child); + ospf_vertex_free(child); + } +} + +static int ospf_spf_remove_link(struct vertex *vertex, struct list *vertex_list, + struct router_lsa_link *link) +{ + struct listnode *node, *inner_node; + struct vertex *child; + struct vertex_parent *vertex_parent; + + /* + * Identify the node who shares a subnet (given by the link) with a + * child and remove the branch of this particular child. + */ + for (ALL_LIST_ELEMENTS_RO(vertex->children, node, child)) { + for (ALL_LIST_ELEMENTS_RO(child->parents, inner_node, + vertex_parent)) { + if ((vertex_parent->local_nexthop->router.s_addr + & link->link_data.s_addr) + == (link->link_id.s_addr + & link->link_data.s_addr)) { + ospf_spf_remove_branch(vertex_parent, child, + vertex_list); + return 0; + } + } + } + + /* No link found yet, move on recursively */ + for (ALL_LIST_ELEMENTS_RO(vertex->children, node, child)) { + if (ospf_spf_remove_link(child, vertex_list, link) == 0) + return 0; + } + + /* link was not removed yet */ + return 1; +} + +void ospf_spf_remove_resource(struct vertex *vertex, struct list *vertex_list, + struct protected_resource *resource) +{ + struct listnode *node, *nnode; + struct vertex *found; + struct vertex_parent *vertex_parent; + + switch (resource->type) { + case OSPF_TI_LFA_LINK_PROTECTION: + ospf_spf_remove_link(vertex, vertex_list, resource->link); + break; + case OSPF_TI_LFA_NODE_PROTECTION: + found = ospf_spf_vertex_find(resource->router_id, vertex_list); + if (!found) + break; + + /* + * Remove the node by removing all links from its parents. Note + * that the child is automatically removed here with the last + * link from a parent, hence no explicit removal of the node. + */ + for (ALL_LIST_ELEMENTS(found->parents, node, nnode, + vertex_parent)) + ospf_spf_remove_branch(vertex_parent, found, + vertex_list); + + break; + case OSPF_TI_LFA_UNDEFINED_PROTECTION: + /* do nothing */ + break; + } +} + +static void ospf_spf_init(struct ospf_area *area, struct ospf_lsa *root_lsa, + bool is_dry_run, bool is_root_node) +{ + struct list *vertex_list; + struct vertex *v; + + /* Create vertex list */ + vertex_list = list_new(); + vertex_list->del = ospf_vertex_free; + area->spf_vertex_list = vertex_list; + + /* Create root node. */ + v = ospf_vertex_new(area, root_lsa); + area->spf = v; + + area->spf_dry_run = is_dry_run; + area->spf_root_node = is_root_node; + + /* Reset ABR and ASBR router counts. */ + area->abr_count = 0; + area->asbr_count = 0; +} + +/* return index of link back to V from W, or -1 if no link found */ +static int ospf_lsa_has_link(struct lsa_header *w, struct lsa_header *v) +{ + unsigned int i, length; + struct router_lsa *rl; + struct network_lsa *nl; + + /* In case of W is Network LSA. */ + if (w->type == OSPF_NETWORK_LSA) { + if (v->type == OSPF_NETWORK_LSA) + return -1; + + nl = (struct network_lsa *)w; + length = (ntohs(w->length) - OSPF_LSA_HEADER_SIZE - 4) / 4; + + for (i = 0; i < length; i++) + if (IPV4_ADDR_SAME(&nl->routers[i], &v->id)) + return i; + return -1; + } + + /* In case of W is Router LSA. */ + if (w->type == OSPF_ROUTER_LSA) { + rl = (struct router_lsa *)w; + + length = ntohs(w->length); + + for (i = 0; i < ntohs(rl->links) + && length >= sizeof(struct router_lsa); + i++, length -= 12) { + switch (rl->link[i].type) { + case LSA_LINK_TYPE_POINTOPOINT: + case LSA_LINK_TYPE_VIRTUALLINK: + /* Router LSA ID. */ + if (v->type == OSPF_ROUTER_LSA + && IPV4_ADDR_SAME(&rl->link[i].link_id, + &v->id)) { + return i; + } + break; + case LSA_LINK_TYPE_TRANSIT: + /* Network LSA ID. */ + if (v->type == OSPF_NETWORK_LSA + && IPV4_ADDR_SAME(&rl->link[i].link_id, + &v->id)) { + return i; + } + break; + case LSA_LINK_TYPE_STUB: + /* Stub can't lead anywhere, carry on */ + continue; + default: + break; + } + } + } + return -1; +} + +/* + * Find the next link after prev_link from v to w. If prev_link is + * NULL, return the first link from v to w. Ignore stub and virtual links; + * these link types will never be returned. + */ +static struct router_lsa_link * +ospf_get_next_link(struct vertex *v, struct vertex *w, + struct router_lsa_link *prev_link) +{ + uint8_t *p; + uint8_t *lim; + uint8_t lsa_type = LSA_LINK_TYPE_TRANSIT; + struct router_lsa_link *l; + + if (w->type == OSPF_VERTEX_ROUTER) + lsa_type = LSA_LINK_TYPE_POINTOPOINT; + + if (prev_link == NULL) + p = ((uint8_t *)v->lsa) + OSPF_LSA_HEADER_SIZE + 4; + else { + p = (uint8_t *)prev_link; + p += (OSPF_ROUTER_LSA_LINK_SIZE + + (prev_link->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); + } + + lim = ((uint8_t *)v->lsa) + ntohs(v->lsa->length); + + while (p < lim) { + l = (struct router_lsa_link *)p; + + p += (OSPF_ROUTER_LSA_LINK_SIZE + + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); + + if (l->m[0].type != lsa_type) + continue; + + if (IPV4_ADDR_SAME(&l->link_id, &w->id)) + return l; + } + + return NULL; +} + +static void ospf_spf_flush_parents(struct vertex *w) +{ + struct vertex_parent *vp; + struct listnode *ln, *nn; + + /* delete the existing nexthops */ + for (ALL_LIST_ELEMENTS(w->parents, ln, nn, vp)) { + list_delete_node(w->parents, ln); + vertex_parent_free(vp); + } +} + +/* + * Consider supplied next-hop for inclusion to the supplied list of + * equal-cost next-hops, adjust list as necessary. + * + * Returns vertex parent pointer if created otherwise `NULL` if it already + * exists. + */ +static struct vertex_parent *ospf_spf_add_parent(struct vertex *v, + struct vertex *w, + struct vertex_nexthop *newhop, + struct vertex_nexthop *newlhop, + unsigned int distance) +{ + struct vertex_parent *vp, *wp; + struct listnode *node; + + /* we must have a newhop, and a distance */ + assert(v && w && newhop); + assert(distance); + + /* + * IFF w has already been assigned a distance, then we shouldn't get + * here unless callers have determined V(l)->W is shortest / + * equal-shortest path (0 is a special case distance (no distance yet + * assigned)). + */ + if (w->distance) + assert(distance <= w->distance); + else + w->distance = distance; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Adding %pI4 as parent of %pI4", __func__, + &v->lsa->id, &w->lsa->id); + + /* + * Adding parent for a new, better path: flush existing parents from W. + */ + if (distance < w->distance) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: distance %d better than %d, flushing existing parents", + __func__, distance, w->distance); + ospf_spf_flush_parents(w); + w->distance = distance; + } + + /* + * new parent is <= existing parents, add it to parent list (if nexthop + * not on parent list) + */ + for (ALL_LIST_ELEMENTS_RO(w->parents, node, wp)) { + if (memcmp(newhop, wp->nexthop, sizeof(*newhop)) == 0) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: ... nexthop already on parent list, skipping add", + __func__); + + return NULL; + } + } + + vp = vertex_parent_new(v, ospf_lsa_has_link(w->lsa, v->lsa), newhop, + newlhop); + listnode_add_sort(w->parents, vp); + + return vp; +} + +static int match_stub_prefix(struct lsa_header *lsa, struct in_addr v_link_addr, + struct in_addr w_link_addr) +{ + uint8_t *p, *lim; + struct router_lsa_link *l = NULL; + struct in_addr masked_lsa_addr; + + if (lsa->type != OSPF_ROUTER_LSA) + return 0; + + p = ((uint8_t *)lsa) + OSPF_LSA_HEADER_SIZE + 4; + lim = ((uint8_t *)lsa) + ntohs(lsa->length); + + while (p < lim) { + l = (struct router_lsa_link *)p; + p += (OSPF_ROUTER_LSA_LINK_SIZE + + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); + + if (l->m[0].type != LSA_LINK_TYPE_STUB) + continue; + + masked_lsa_addr.s_addr = + (l->link_id.s_addr & l->link_data.s_addr); + + /* check that both links belong to the same stub subnet */ + if ((masked_lsa_addr.s_addr + == (v_link_addr.s_addr & l->link_data.s_addr)) + && (masked_lsa_addr.s_addr + == (w_link_addr.s_addr & l->link_data.s_addr))) + return 1; + } + + return 0; +} + +/* + * 16.1.1. Calculate nexthop from root through V (parent) to + * vertex W (destination), with given distance from root->W. + * + * The link must be supplied if V is the root vertex. In all other cases + * it may be NULL. + * + * Note that this function may fail, hence the state of the destination + * vertex, W, should /not/ be modified in a dependent manner until + * this function returns. This function will update the W vertex with the + * provided distance as appropriate. + */ +static unsigned int ospf_nexthop_calculation(struct ospf_area *area, + struct vertex *v, struct vertex *w, + struct router_lsa_link *l, + unsigned int distance, int lsa_pos) +{ + struct listnode *node, *nnode; + struct vertex_nexthop *nh, *lnh; + struct vertex_parent *vp; + unsigned int added = 0; + + if (IS_DEBUG_OSPF_EVENT) { + zlog_debug("%s: Start", __func__); + ospf_vertex_dump("V (parent):", v, 1, 1); + ospf_vertex_dump("W (dest) :", w, 1, 1); + zlog_debug("V->W distance: %d", distance); + } + + if (v == area->spf) { + /* + * 16.1.1 para 4. In the first case, the parent vertex (V) is + * the root (the calculating router itself). This means that + * the destination is either a directly connected network or + * directly connected router. The outgoing interface in this + * case is simply the OSPF interface connecting to the + * destination network/router. + */ + + /* we *must* be supplied with the link data */ + assert(l != NULL); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: considering link type:%d link_id:%pI4 link_data:%pI4", + __func__, l->m[0].type, &l->link_id, + &l->link_data); + + if (w->type == OSPF_VERTEX_ROUTER) { + /* + * l is a link from v to w l2 will be link from w to v + */ + struct router_lsa_link *l2 = NULL; + + if (l->m[0].type == LSA_LINK_TYPE_POINTOPOINT) { + struct ospf_interface *oi = NULL; + struct in_addr nexthop = {.s_addr = 0}; + + if (area->spf_root_node) { + oi = ospf_if_lookup_by_lsa_pos(area, + lsa_pos); + if (!oi) { + zlog_debug( + "%s: OI not found in LSA: lsa_pos: %d link_id:%pI4 link_data:%pI4", + __func__, lsa_pos, + &l->link_id, + &l->link_data); + return 0; + } + } + + /* + * If the destination is a router which connects + * to the calculating router via a + * Point-to-MultiPoint network, the + * destination's next hop IP address(es) can be + * determined by examining the destination's + * router-LSA: each link pointing back to the + * calculating router and having a Link Data + * field belonging to the Point-to-MultiPoint + * network provides an IP address of the next + * hop router. + * + * At this point l is a link from V to W, and V + * is the root ("us"). If it is a point-to- + * multipoint interface, then look through the + * links in the opposite direction (W to V). + * If any of them have an address that lands + * within the subnet declared by the PtMP link, + * then that link is a constituent of the PtMP + * link, and its address is a nexthop address + * for V. + * + * Note for point-to-point interfaces: + * + * Having nexthop = 0 (as proposed in the RFC) + * is tempting, but NOT acceptable. It breaks + * AS-External routes with a forwarding address, + * since ospf_ase_complete_direct_routes() will + * mistakenly assume we've reached the last hop + * and should place the forwarding address as + * nexthop. Also, users may configure multi- + * access links in p2p mode, so we need the IP + * to ARP the nexthop. + * + * If the calculating router is the SPF root + * node and the link is P2P then access the + * interface information directly. This can be + * crucial when e.g. IP unnumbered is used + * where 'correct' nexthop information are not + * available via Router LSAs. + * + * Otherwise handle P2P and P2MP the same way + * as described above using a reverse lookup to + * figure out the nexthop. + */ + + /* + * HACK: we don't know (yet) how to distinguish + * between P2P and P2MP interfaces by just + * looking at LSAs, which is important for + * TI-LFA since you want to do SPF calculations + * from the perspective of other nodes. Since + * TI-LFA is currently not implemented for P2MP + * we just check here if it is enabled and then + * blindly assume that P2P is used. Ultimately + * the interface code needs to be removed + * somehow. + */ + if (area->ospf->ti_lfa_enabled + || (oi && oi->type == OSPF_IFTYPE_POINTOPOINT) + || (oi && oi->type == OSPF_IFTYPE_POINTOMULTIPOINT + && oi->address->prefixlen == IPV4_MAX_BITLEN)) { + struct ospf_neighbor *nbr_w = NULL; + + /* Calculating node is root node, link + * is P2P */ + if (area->spf_root_node) { + nbr_w = ospf_nbr_lookup_by_routerid( + oi->nbrs, &l->link_id); + if (nbr_w) { + added = 1; + nexthop = nbr_w->src; + } + } + + /* Reverse lookup */ + if (!added) { + while ((l2 = ospf_get_next_link( + w, v, l2))) { + if (match_stub_prefix( + v->lsa, + l->link_data, + l2->link_data)) { + added = 1; + nexthop = + l2->link_data; + break; + } + } + } + } else if (oi && oi->type + == OSPF_IFTYPE_POINTOMULTIPOINT) { + struct prefix_ipv4 la; + + la.family = AF_INET; + la.prefixlen = oi->address->prefixlen; + + /* + * V links to W on PtMP interface; + * find the interface address on W + */ + while ((l2 = ospf_get_next_link(w, v, + l2))) { + la.prefix = l2->link_data; + + if (prefix_cmp((struct prefix + *)&la, + oi->address) + != 0) + continue; + added = 1; + nexthop = l2->link_data; + break; + } + } + + if (added) { + nh = vertex_nexthop_new(); + nh->router = nexthop; + nh->lsa_pos = lsa_pos; + + /* + * Since v is the root the nexthop and + * local nexthop are the same. + */ + lnh = vertex_nexthop_new(); + memcpy(lnh, nh, + sizeof(struct vertex_nexthop)); + + if (ospf_spf_add_parent(v, w, nh, lnh, + distance) == + NULL) { + vertex_nexthop_free(nh); + vertex_nexthop_free(lnh); + } + return 1; + } else + zlog_info( + "%s: could not determine nexthop for link %s", + __func__, oi ? oi->ifp->name : ""); + } /* end point-to-point link from V to W */ + else if (l->m[0].type == LSA_LINK_TYPE_VIRTUALLINK) { + /* + * VLink implementation limitations: + * a) vl_data can only reference one nexthop, + * so no ECMP to backbone through VLinks. + * Though transit-area summaries may be + * considered, and those can be ECMP. + * b) We can only use /one/ VLink, even if + * multiple ones exist this router through + * multiple transit-areas. + */ + + struct ospf_vl_data *vl_data; + + vl_data = ospf_vl_lookup(area->ospf, NULL, + l->link_id); + + if (vl_data + && CHECK_FLAG(vl_data->flags, + OSPF_VL_FLAG_APPROVED)) { + nh = vertex_nexthop_new(); + nh->router = vl_data->nexthop.router; + nh->lsa_pos = vl_data->nexthop.lsa_pos; + + /* + * Since v is the root the nexthop and + * local nexthop are the same. + */ + lnh = vertex_nexthop_new(); + memcpy(lnh, nh, + sizeof(struct vertex_nexthop)); + + if (ospf_spf_add_parent(v, w, nh, lnh, + distance) == + NULL) { + vertex_nexthop_free(nh); + vertex_nexthop_free(lnh); + } + + return 1; + } else + zlog_info( + "%s: vl_data for VL link not found", + __func__); + } /* end virtual-link from V to W */ + return 0; + } /* end W is a Router vertex */ + else { + assert(w->type == OSPF_VERTEX_NETWORK); + + nh = vertex_nexthop_new(); + nh->router.s_addr = 0; /* Nexthop not required */ + nh->lsa_pos = lsa_pos; + + /* + * Since v is the root the nexthop and + * local nexthop are the same. + */ + lnh = vertex_nexthop_new(); + memcpy(lnh, nh, sizeof(struct vertex_nexthop)); + + if (ospf_spf_add_parent(v, w, nh, lnh, distance) == + NULL) { + vertex_nexthop_free(nh); + vertex_nexthop_free(lnh); + } + + return 1; + } + } /* end V is the root */ + /* Check if W's parent is a network connected to root. */ + else if (v->type == OSPF_VERTEX_NETWORK) { + /* See if any of V's parents are the root. */ + for (ALL_LIST_ELEMENTS(v->parents, node, nnode, vp)) { + if (vp->parent == area->spf) { + /* + * 16.1.1 para 5. ...the parent vertex is a + * network that directly connects the + * calculating router to the destination + * router. The list of next hops is then + * determined by examining the destination's + * router-LSA ... + */ + + assert(w->type == OSPF_VERTEX_ROUTER); + while ((l = ospf_get_next_link(w, v, l))) { + /* + * ... For each link in the router-LSA + * that points back to the parent + * network, the link's Link Data field + * provides the IP address of a next hop + * router. The outgoing interface to use + * can then be derived from the next + * hop IP address (or it can be + * inherited from the parent network). + */ + nh = vertex_nexthop_new(); + nh->router = l->link_data; + nh->lsa_pos = vp->nexthop->lsa_pos; + + /* + * Since v is the root the nexthop and + * local nexthop are the same. + */ + lnh = vertex_nexthop_new(); + memcpy(lnh, nh, + sizeof(struct vertex_nexthop)); + + added = 1; + if (ospf_spf_add_parent(v, w, nh, lnh, + distance) == + NULL) { + vertex_nexthop_free(nh); + vertex_nexthop_free(lnh); + } + } + /* + * Note lack of return is deliberate. See next + * comment. + */ + } + } + /* + * NB: This code is non-trivial. + * + * E.g. it is not enough to know that V connects to the root. It + * is also important that the while above, looping through all + * links from W->V found at least one link, so that we know + * there is bi-directional connectivity between V and W (which + * need not be the case, e.g. when OSPF has not yet converged + * fully). Otherwise, if we /always/ return here, without having + * checked that root->V->-W actually resulted in a valid nexthop + * being created, then we we will prevent SPF from finding/using + * higher cost paths. + * + * It is important, if root->V->W has not been added, that we + * continue through to the intervening-router nexthop code + * below. So as to ensure other paths to V may be used. This + * avoids unnecessary blackholes while OSPF is converging. + * + * I.e. we may have arrived at this function, examining V -> W, + * via workable paths other than root -> V, and it's important + * to avoid getting "confused" by non-working root->V->W path + * - it's important to *not* lose the working non-root paths, + * just because of a non-viable root->V->W. + */ + if (added) + return added; + } + + /* + * 16.1.1 para 4. If there is at least one intervening router in the + * current shortest path between the destination and the root, the + * destination simply inherits the set of next hops from the + * parent. + */ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Intervening routers, adding parent(s)", + __func__); + + for (ALL_LIST_ELEMENTS(v->parents, node, nnode, vp)) { + added = 1; + + /* + * The nexthop is inherited, but the local nexthop still needs + * to be created. + */ + if (l) { + lnh = vertex_nexthop_new(); + lnh->router = l->link_data; + lnh->lsa_pos = lsa_pos; + } else { + lnh = NULL; + } + + nh = vertex_nexthop_new(); + *nh = *vp->nexthop; + + if (ospf_spf_add_parent(v, w, nh, lnh, distance) == NULL) { + vertex_nexthop_free(nh); + vertex_nexthop_free(lnh); + } + } + + return added; +} + +static int ospf_spf_is_protected_resource(struct ospf_area *area, + struct router_lsa_link *link, + struct lsa_header *lsa) +{ + uint8_t *p, *lim; + struct router_lsa_link *p_link; + struct router_lsa_link *l = NULL; + struct in_addr router_id; + int link_type; + + if (!area->spf_protected_resource) + return 0; + + link_type = link->m[0].type; + + switch (area->spf_protected_resource->type) { + case OSPF_TI_LFA_LINK_PROTECTION: + p_link = area->spf_protected_resource->link; + if (!p_link) + return 0; + + /* For P2P: check if the link belongs to the same subnet */ + if (link_type == LSA_LINK_TYPE_POINTOPOINT + && (p_link->link_id.s_addr & p_link->link_data.s_addr) + == (link->link_data.s_addr + & p_link->link_data.s_addr)) + return 1; + + /* For stub: check if this the same subnet */ + if (link_type == LSA_LINK_TYPE_STUB + && (p_link->link_id.s_addr == link->link_id.s_addr) + && (p_link->link_data.s_addr == link->link_data.s_addr)) + return 1; + + break; + case OSPF_TI_LFA_NODE_PROTECTION: + router_id = area->spf_protected_resource->router_id; + if (router_id.s_addr == INADDR_ANY) + return 0; + + /* For P2P: check if the link leads to the protected node */ + if (link_type == LSA_LINK_TYPE_POINTOPOINT + && link->link_id.s_addr == router_id.s_addr) + return 1; + + /* The rest is about stub links! */ + if (link_type != LSA_LINK_TYPE_STUB) + return 0; + + /* + * Check if there's a P2P link in the router LSA with the + * corresponding link data in the same subnet. + */ + + p = ((uint8_t *)lsa) + OSPF_LSA_HEADER_SIZE + 4; + lim = ((uint8_t *)lsa) + ntohs(lsa->length); + + while (p < lim) { + l = (struct router_lsa_link *)p; + p += (OSPF_ROUTER_LSA_LINK_SIZE + + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); + + /* We only care about P2P with the proper link id */ + if ((l->m[0].type != LSA_LINK_TYPE_POINTOPOINT) + || (l->link_id.s_addr != router_id.s_addr)) + continue; + + /* Link data in the subnet given by the link? */ + if ((link->link_id.s_addr & link->link_data.s_addr) + == (l->link_data.s_addr & link->link_data.s_addr)) + return 1; + } + + break; + case OSPF_TI_LFA_UNDEFINED_PROTECTION: + break; + } + + return 0; +} + +/* + * For TI-LFA we need the reverse SPF for Q spaces. The reverse SPF is created + * by honoring the weight of the reverse 'edge', e.g. the edge from W to V, and + * NOT the weight of the 'edge' from V to W as usual. Hence we need to find the + * corresponding link in the LSA of W and extract the particular weight. + * + * TODO: Only P2P supported by now! + */ +static uint16_t get_reverse_distance(struct vertex *v, + struct router_lsa_link *l, + struct ospf_lsa *w_lsa) +{ + uint8_t *p, *lim; + struct router_lsa_link *w_link; + uint16_t distance = 0; + + assert(w_lsa && w_lsa->data); + + p = ((uint8_t *)w_lsa->data) + OSPF_LSA_HEADER_SIZE + 4; + lim = ((uint8_t *)w_lsa->data) + ntohs(w_lsa->data->length); + + while (p < lim) { + w_link = (struct router_lsa_link *)p; + p += (OSPF_ROUTER_LSA_LINK_SIZE + + (w_link->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); + + /* Only care about P2P with link ID equal to V's router id */ + if (w_link->m[0].type == LSA_LINK_TYPE_POINTOPOINT + && w_link->link_id.s_addr == v->id.s_addr) { + distance = ntohs(w_link->m[0].metric); + break; + } + } + + /* + * This might happen if the LSA for W is not complete yet. In this + * case we take the weight of the 'forward' link from V. When the LSA + * for W is completed the reverse SPF is run again anyway. + */ + if (distance == 0) + distance = ntohs(l->m[0].metric); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: reversed distance is %u", __func__, distance); + + return distance; +} + +/* + * RFC2328 16.1 (2). + * v is on the SPF tree. Examine the links in v's LSA. Update the list of + * candidates with any vertices not already on the list. If a lower-cost path + * is found to a vertex already on the candidate list, store the new cost. + */ +static void ospf_spf_next(struct vertex *v, struct ospf_area *area, + struct vertex_pqueue_head *candidate) +{ + struct ospf_lsa *w_lsa = NULL; + uint8_t *p; + uint8_t *lim; + struct router_lsa_link *l = NULL; + struct in_addr *r; + int type = 0, lsa_pos = -1, lsa_pos_next = 0; + uint16_t link_distance; + + /* + * If this is a router-LSA, and bit V of the router-LSA (see Section + * A.4.2:RFC2328) is set, set Area A's TransitCapability to true. + */ + if (v->type == OSPF_VERTEX_ROUTER) { + if (IS_ROUTER_LSA_VIRTUAL((struct router_lsa *)v->lsa)) + area->transit = OSPF_TRANSIT_TRUE; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Next vertex of %s vertex %pI4", __func__, + v->type == OSPF_VERTEX_ROUTER ? "Router" : "Network", + &v->lsa->id); + + p = ((uint8_t *)v->lsa) + OSPF_LSA_HEADER_SIZE + 4; + lim = ((uint8_t *)v->lsa) + ntohs(v->lsa->length); + + while (p < lim) { + struct vertex *w; + unsigned int distance; + + /* In case of V is Router-LSA. */ + if (v->lsa->type == OSPF_ROUTER_LSA) { + l = (struct router_lsa_link *)p; + + lsa_pos = lsa_pos_next; /* LSA link position */ + lsa_pos_next++; + + p += (OSPF_ROUTER_LSA_LINK_SIZE + + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); + + /* + * (a) If this is a link to a stub network, examine the + * next link in V's LSA. Links to stub networks will + * be considered in the second stage of the shortest + * path calculation. + */ + if ((type = l->m[0].type) == LSA_LINK_TYPE_STUB) + continue; + + /* + * Don't process TI-LFA protected resources. + * + * TODO: Replace this by a proper solution, e.g. remove + * corresponding links from the LSDB and run the SPF + * algo with the stripped-down LSDB. + */ + if (ospf_spf_is_protected_resource(area, l, v->lsa)) + continue; + + /* + * (b) Otherwise, W is a transit vertex (router or + * transit network). Look up the vertex W's LSA + * (router-LSA or network-LSA) in Area A's link state + * database. + */ + switch (type) { + case LSA_LINK_TYPE_POINTOPOINT: + case LSA_LINK_TYPE_VIRTUALLINK: + if (type == LSA_LINK_TYPE_VIRTUALLINK + && IS_DEBUG_OSPF_EVENT) + zlog_debug( + "looking up LSA through VL: %pI4", + &l->link_id); + w_lsa = ospf_lsa_lookup(area->ospf, area, + OSPF_ROUTER_LSA, + l->link_id, l->link_id); + if (w_lsa && IS_DEBUG_OSPF_EVENT) + zlog_debug("found Router LSA %pI4", + &l->link_id); + break; + case LSA_LINK_TYPE_TRANSIT: + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Looking up Network LSA, ID: %pI4", + &l->link_id); + w_lsa = ospf_lsa_lookup_by_id( + area, OSPF_NETWORK_LSA, l->link_id); + if (w_lsa && IS_DEBUG_OSPF_EVENT) + zlog_debug("found the LSA"); + break; + default: + flog_warn(EC_OSPF_LSA, + "Invalid LSA link type %d", type); + continue; + } + + /* + * For TI-LFA we might need the reverse SPF. + * Currently only works with P2P! + */ + if (type == LSA_LINK_TYPE_POINTOPOINT + && area->spf_reversed) + link_distance = + get_reverse_distance(v, l, w_lsa); + else + link_distance = ntohs(l->m[0].metric); + + /* step (d) below */ + distance = v->distance + link_distance; + } else { + /* In case of V is Network-LSA. */ + r = (struct in_addr *)p; + p += sizeof(struct in_addr); + + /* Lookup the vertex W's LSA. */ + w_lsa = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA, + *r); + if (w_lsa && IS_DEBUG_OSPF_EVENT) + zlog_debug("found Router LSA %pI4", + &w_lsa->data->id); + + /* step (d) below */ + distance = v->distance; + } + + /* + * (b cont.) If the LSA does not exist, or its LS age is equal + * to MaxAge, or it does not have a link back to vertex V, + * examine the next link in V's LSA.[23] + */ + if (w_lsa == NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("No LSA found"); + continue; + } + + if (IS_LSA_MAXAGE(w_lsa)) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("LSA is MaxAge"); + continue; + } + + if (ospf_lsa_has_link(w_lsa->data, v->lsa) < 0) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("The LSA doesn't have a link back"); + continue; + } + + /* + * (c) If vertex W is already on the shortest-path tree, examine + * the next link in the LSA. + */ + if (w_lsa->stat == LSA_SPF_IN_SPFTREE) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("The LSA is already in SPF"); + continue; + } + + /* + * (d) Calculate the link state cost D of the resulting path + * from the root to vertex W. D is equal to the sum of the link + * state cost of the (already calculated) shortest path to + * vertex V and the advertised cost of the link between vertices + * V and W. If D is: + */ + + /* calculate link cost D -- moved above */ + + /* Is there already vertex W in candidate list? */ + if (w_lsa->stat == LSA_SPF_NOT_EXPLORED) { + /* prepare vertex W. */ + w = ospf_vertex_new(area, w_lsa); + + /* Calculate nexthop to W. */ + if (ospf_nexthop_calculation(area, v, w, l, distance, + lsa_pos)) + vertex_pqueue_add(candidate, w); + else { + listnode_delete(area->spf_vertex_list, w); + ospf_vertex_free(w); + w_lsa->stat = LSA_SPF_NOT_EXPLORED; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Nexthop Calc failed"); + } + } else if (w_lsa->stat != LSA_SPF_IN_SPFTREE) { + w = w_lsa->stat; + if (w->distance < distance) { + continue; + } + else if (w->distance == distance) { + /* + * Found an equal-cost path to W. + * Calculate nexthop of to W from V. + */ + ospf_nexthop_calculation(area, v, w, l, + distance, lsa_pos); + } + else { + /* + * Found a lower-cost path to W. + * nexthop_calculation is conditional, if it + * finds valid nexthop it will call + * spf_add_parents, which will flush the old + * parents. + */ + vertex_pqueue_del(candidate, w); + ospf_nexthop_calculation(area, v, w, l, + distance, lsa_pos); + vertex_pqueue_add(candidate, w); + } + } /* end W is already on the candidate list */ + } /* end loop over the links in V's LSA */ +} + +static void ospf_spf_dump(struct vertex *v, int i) +{ + struct listnode *cnode; + struct listnode *nnode; + struct vertex_parent *parent; + + if (v->type == OSPF_VERTEX_ROUTER) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("SPF Result: %d [R] %pI4", i, + &v->lsa->id); + } else { + struct network_lsa *lsa = (struct network_lsa *)v->lsa; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("SPF Result: %d [N] %pI4/%d", i, + &v->lsa->id, + ip_masklen(lsa->mask)); + } + + if (IS_DEBUG_OSPF_EVENT) + for (ALL_LIST_ELEMENTS_RO(v->parents, nnode, parent)) { + zlog_debug(" nexthop %p %pI4 %d", + (void *)parent->nexthop, + &parent->nexthop->router, + parent->nexthop->lsa_pos); + } + + i++; + + for (ALL_LIST_ELEMENTS_RO(v->children, cnode, v)) + ospf_spf_dump(v, i); +} + +void ospf_spf_print(struct vty *vty, struct vertex *v, int i) +{ + struct listnode *cnode; + struct listnode *nnode; + struct vertex_parent *parent; + + if (v->type == OSPF_VERTEX_ROUTER) { + vty_out(vty, "SPF Result: depth %d [R] %pI4\n", i, &v->lsa->id); + } else { + struct network_lsa *lsa = (struct network_lsa *)v->lsa; + vty_out(vty, "SPF Result: depth %d [N] %pI4/%d\n", i, + &v->lsa->id, ip_masklen(lsa->mask)); + } + + for (ALL_LIST_ELEMENTS_RO(v->parents, nnode, parent)) { + vty_out(vty, + " nexthop %pI4 lsa pos %d -- local nexthop %pI4 lsa pos %d\n", + &parent->nexthop->router, parent->nexthop->lsa_pos, + &parent->local_nexthop->router, + parent->local_nexthop->lsa_pos); + } + + i++; + + for (ALL_LIST_ELEMENTS_RO(v->children, cnode, v)) + ospf_spf_print(vty, v, i); +} + +/* Second stage of SPF calculation. */ +static void ospf_spf_process_stubs(struct ospf_area *area, struct vertex *v, + struct route_table *rt, int parent_is_root) +{ + struct listnode *cnode, *cnnode; + struct vertex *child; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: processing stubs for area %pI4", __func__, + &area->area_id); + + if (v->type == OSPF_VERTEX_ROUTER) { + uint8_t *p; + uint8_t *lim; + struct router_lsa_link *l; + struct router_lsa *router_lsa; + int lsa_pos = 0; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: processing router LSA, id: %pI4", + __func__, &v->lsa->id); + + router_lsa = (struct router_lsa *)v->lsa; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: we have %d links to process", __func__, + ntohs(router_lsa->links)); + + p = ((uint8_t *)v->lsa) + OSPF_LSA_HEADER_SIZE + 4; + lim = ((uint8_t *)v->lsa) + ntohs(v->lsa->length); + + while (p < lim) { + l = (struct router_lsa_link *)p; + + p += (OSPF_ROUTER_LSA_LINK_SIZE + + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); + + /* Don't process TI-LFA protected resources */ + if (l->m[0].type == LSA_LINK_TYPE_STUB + && !ospf_spf_is_protected_resource(area, l, v->lsa)) + ospf_intra_add_stub(rt, l, v, area, + parent_is_root, lsa_pos); + lsa_pos++; + } + } + + ospf_vertex_dump("ospf_process_stubs(): after examining links: ", v, 1, + 1); + + for (ALL_LIST_ELEMENTS(v->children, cnode, cnnode, child)) { + if (CHECK_FLAG(child->flags, OSPF_VERTEX_PROCESSED)) + continue; + + /* + * The first level of routers connected to the root + * should have 'parent_is_root' set, including those + * connected via a network vertex. + */ + if (area->spf == v) + parent_is_root = 1; + else if (v->type == OSPF_VERTEX_ROUTER) + parent_is_root = 0; + + ospf_spf_process_stubs(area, child, rt, parent_is_root); + + SET_FLAG(child->flags, OSPF_VERTEX_PROCESSED); + } +} + +void ospf_rtrs_free(struct route_table *rtrs) +{ + struct route_node *rn; + struct list *or_list; + struct ospf_route * or ; + struct listnode *node, *nnode; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Route: Router Routing Table free"); + + for (rn = route_top(rtrs); rn; rn = route_next(rn)) + if ((or_list = rn->info) != NULL) { + for (ALL_LIST_ELEMENTS(or_list, node, nnode, or)) + ospf_route_free(or); + + list_delete(&or_list); + + /* Unlock the node. */ + rn->info = NULL; + route_unlock_node(rn); + } + + route_table_finish(rtrs); +} + +void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list) +{ + /* + * Free nexthop information, canonical versions of which are + * attached the first level of router vertices attached to the + * root vertex, see ospf_nexthop_calculation. + */ + if (spf) + ospf_canonical_nexthops_free(spf); + + /* Free SPF vertices list with deconstructor ospf_vertex_free. */ + if (vertex_list) + list_delete(&vertex_list); +} + +/* Calculating the shortest-path tree for an area, see RFC2328 16.1. */ +void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa, + struct route_table *new_table, + struct route_table *all_rtrs, + struct route_table *new_rtrs, bool is_dry_run, + bool is_root_node) +{ + struct vertex_pqueue_head candidate; + struct vertex *v; + + if (IS_DEBUG_OSPF_EVENT) { + zlog_debug("%s: Start: running Dijkstra for area %pI4", + __func__, &area->area_id); + } + + /* + * If the router LSA of the root is not yet allocated, return this + * area's calculation. In the 'usual' case the root_lsa is the + * self-originated router LSA of the node itself. + */ + if (!root_lsa) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: Skip area %pI4's calculation due to empty root LSA", + __func__, &area->area_id); + return; + } + + /* Initialize the algorithm's data structures, see RFC2328 16.1. (1). */ + + /* + * This function scans all the LSA database and set the stat field to + * LSA_SPF_NOT_EXPLORED. + */ + lsdb_clean_stat(area->lsdb); + + /* Create a new heap for the candidates. */ + vertex_pqueue_init(&candidate); + + /* + * Initialize the shortest-path tree to only the root (which is usually + * the router doing the calculation). + */ + ospf_spf_init(area, root_lsa, is_dry_run, is_root_node); + + /* Set Area A's TransitCapability to false. */ + area->transit = OSPF_TRANSIT_FALSE; + area->shortcut_capability = 1; + + /* + * Use the root vertex for the start of the SPF algorithm and make it + * part of the tree. + */ + v = area->spf; + v->lsa_p->stat = LSA_SPF_IN_SPFTREE; + + for (;;) { + /* RFC2328 16.1. (2). */ + ospf_spf_next(v, area, &candidate); + + /* RFC2328 16.1. (3). */ + v = vertex_pqueue_pop(&candidate); + if (!v) + /* No more vertices left. */ + break; + + v->lsa_p->stat = LSA_SPF_IN_SPFTREE; + + ospf_vertex_add_parent(v); + + /* RFC2328 16.1. (4). */ + if (v->type != OSPF_VERTEX_ROUTER) + ospf_intra_add_transit(new_table, v, area); + else { + if (new_rtrs) + ospf_intra_add_router(new_rtrs, v, area, false); + if (all_rtrs) + ospf_intra_add_router(all_rtrs, v, area, true); + } + + /* Iterate back to (2), see RFC2328 16.1. (5). */ + } + + if (IS_DEBUG_OSPF_EVENT) { + ospf_spf_dump(area->spf, 0); + ospf_route_table_dump(new_table); + if (all_rtrs) + ospf_router_route_table_dump(all_rtrs); + } + + /* + * Second stage of SPF calculation procedure's, add leaves to the tree + * for stub networks. + */ + ospf_spf_process_stubs(area, area->spf, new_table, 0); + + ospf_vertex_dump(__func__, area->spf, 0, 1); + + /* Increment SPF Calculation Counter. */ + area->spf_calculation++; + + monotime(&area->ospf->ts_spf); + area->ts_spf = area->ospf->ts_spf; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Stop. %zd vertices", __func__, + mtype_stats_alloc(MTYPE_OSPF_VERTEX)); +} + +void ospf_spf_calculate_area(struct ospf *ospf, struct ospf_area *area, + struct route_table *new_table, + struct route_table *all_rtrs, + struct route_table *new_rtrs) +{ + ospf_spf_calculate(area, area->router_lsa_self, new_table, all_rtrs, + new_rtrs, false, true); + + if (ospf->ti_lfa_enabled) + ospf_ti_lfa_compute(area, new_table, + ospf->ti_lfa_protection_type); + + ospf_spf_cleanup(area->spf, area->spf_vertex_list); + + area->spf = NULL; + area->spf_vertex_list = NULL; +} + +void ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table, + struct route_table *all_rtrs, + struct route_table *new_rtrs) +{ + struct ospf_area *area; + struct listnode *node, *nnode; + + /* Calculate SPF for each area. */ + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + /* Do backbone last, so as to first discover intra-area paths + * for any back-bone virtual-links */ + if (ospf->backbone && ospf->backbone == area) + continue; + + ospf_spf_calculate_area(ospf, area, new_table, all_rtrs, + new_rtrs); + } + + /* SPF for backbone, if required */ + if (ospf->backbone) + ospf_spf_calculate_area(ospf, ospf->backbone, new_table, + all_rtrs, new_rtrs); +} + +/* Worker for SPF calculation scheduler. */ +static void ospf_spf_calculate_schedule_worker(struct event *thread) +{ + struct ospf *ospf = EVENT_ARG(thread); + struct route_table *new_table, *new_rtrs; + struct route_table *all_rtrs = NULL; + struct timeval start_time, spf_start_time; + unsigned long ia_time, prune_time, rt_time; + unsigned long abr_time, total_spf_time, spf_time; + char rbuf[32]; /* reason_buf */ + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("SPF: Timer (SPF calculation expire)"); + + ospf->t_spf_calc = NULL; + + ospf_vl_unapprove(ospf); + + /* Execute SPF for each area including backbone, see RFC 2328 16.1. */ + monotime(&spf_start_time); + new_table = route_table_init(); /* routing table */ + new_rtrs = route_table_init(); /* ABR/ASBR routing table */ + + /* If we have opaque enabled then track all router reachability */ + if (CHECK_FLAG(ospf->opaque, OPAQUE_OPERATION_READY_BIT)) + all_rtrs = route_table_init(); + + ospf_spf_calculate_areas(ospf, new_table, all_rtrs, new_rtrs); + spf_time = monotime_since(&spf_start_time, NULL); + + ospf_vl_shut_unapproved(ospf); + + /* Calculate inter-area routes, see RFC 2328 16.2. */ + monotime(&start_time); + ospf_ia_routing(ospf, new_table, new_rtrs); + ia_time = monotime_since(&start_time, NULL); + + /* Get rid of transit networks and routers we cannot reach anyway. */ + monotime(&start_time); + ospf_prune_unreachable_networks(new_table); + if (all_rtrs) + ospf_prune_unreachable_routers(all_rtrs); + ospf_prune_unreachable_routers(new_rtrs); + prune_time = monotime_since(&start_time, NULL); + + /* Note: RFC 2328 16.3. is apparently missing. */ + + /* + * Calculate AS external routes, see RFC 2328 16.4. + * There is a dedicated routing table for external routes which is not + * handled here directly + */ + ospf_ase_calculate_schedule(ospf); + ospf_ase_calculate_timer_add(ospf); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: ospf install new route, vrf %s id %u new_table count %lu", + __func__, ospf_vrf_id_to_name(ospf->vrf_id), + ospf->vrf_id, new_table->count); + + /* Update routing table. */ + monotime(&start_time); + ospf_route_install(ospf, new_table); + rt_time = monotime_since(&start_time, NULL); + + /* Free old all routers routing table */ + if (ospf->oall_rtrs) { + ospf_rtrs_free(ospf->oall_rtrs); + ospf->oall_rtrs = NULL; + } + + /* Update all routers routing table */ + ospf->oall_rtrs = ospf->all_rtrs; + ospf->all_rtrs = all_rtrs; +#ifdef SUPPORT_OSPF_API + ospf_apiserver_notify_reachable(ospf->oall_rtrs, ospf->all_rtrs); +#endif + + /* Free old ABR/ASBR routing table */ + if (ospf->old_rtrs) { + ospf_rtrs_free(ospf->old_rtrs); + ospf->old_rtrs = NULL; + } + + /* Update ABR/ASBR routing table */ + ospf->old_rtrs = ospf->new_rtrs; + ospf->new_rtrs = new_rtrs; + + /* ABRs may require additional changes, see RFC 2328 16.7. */ + monotime(&start_time); + if (IS_OSPF_ABR(ospf)) { + if (ospf->anyNSSA) + ospf_abr_nssa_check_status(ospf); + ospf_abr_task(ospf); + } + abr_time = monotime_since(&start_time, NULL); + + /* Schedule Segment Routing update */ + ospf_sr_update_task(ospf); + + total_spf_time = + monotime_since(&spf_start_time, &ospf->ts_spf_duration); + + rbuf[0] = '\0'; + if (spf_reason_flags) { + if (spf_reason_flags & (1 << SPF_FLAG_ROUTER_LSA_INSTALL)) + strlcat(rbuf, "R, ", sizeof(rbuf)); + if (spf_reason_flags & (1 << SPF_FLAG_NETWORK_LSA_INSTALL)) + strlcat(rbuf, "N, ", sizeof(rbuf)); + if (spf_reason_flags & (1 << SPF_FLAG_SUMMARY_LSA_INSTALL)) + strlcat(rbuf, "S, ", sizeof(rbuf)); + if (spf_reason_flags & (1 << SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL)) + strlcat(rbuf, "AS, ", sizeof(rbuf)); + if (spf_reason_flags & (1 << SPF_FLAG_ABR_STATUS_CHANGE)) + strlcat(rbuf, "ABR, ", sizeof(rbuf)); + if (spf_reason_flags & (1 << SPF_FLAG_ASBR_STATUS_CHANGE)) + strlcat(rbuf, "ASBR, ", sizeof(rbuf)); + if (spf_reason_flags & (1 << SPF_FLAG_MAXAGE)) + strlcat(rbuf, "M, ", sizeof(rbuf)); + if (spf_reason_flags & (1 << SPF_FLAG_GR_FINISH)) + strlcat(rbuf, "GR, ", sizeof(rbuf)); + + size_t rbuflen = strlen(rbuf); + if (rbuflen >= 2) + rbuf[rbuflen - 2] = '\0'; /* skip the last ", " */ + else + rbuf[0] = '\0'; + } + + if (IS_DEBUG_OSPF_EVENT) { + zlog_info("SPF Processing Time(usecs): %ld", total_spf_time); + zlog_info(" SPF Time: %ld", spf_time); + zlog_info(" InterArea: %ld", ia_time); + zlog_info(" Prune: %ld", prune_time); + zlog_info(" RouteInstall: %ld", rt_time); + if (IS_OSPF_ABR(ospf)) + zlog_info(" ABR: %ld (%d areas)", + abr_time, ospf->areas->count); + zlog_info("Reason(s) for SPF: %s", rbuf); + } + + ospf_clear_spf_reason_flags(); +} + +/* + * Add schedule for SPF calculation. To avoid frequenst SPF calc, we set timer + * for SPF calc. + */ +void ospf_spf_calculate_schedule(struct ospf *ospf, ospf_spf_reason_t reason) +{ + unsigned long delay, elapsed, ht; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("SPF: calculation timer scheduled"); + + /* OSPF instance does not exist. */ + if (ospf == NULL) + return; + + ospf_spf_set_reason(reason); + + /* SPF calculation timer is already scheduled. */ + if (ospf->t_spf_calc) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "SPF: calculation timer is already scheduled: %p", + (void *)ospf->t_spf_calc); + return; + } + + elapsed = monotime_since(&ospf->ts_spf, NULL) / 1000; + + ht = ospf->spf_holdtime * ospf->spf_hold_multiplier; + + if (ht > ospf->spf_max_holdtime) + ht = ospf->spf_max_holdtime; + + /* Get SPF calculation delay time. */ + if (elapsed < ht) { + /* + * Got an event within the hold time of last SPF. We need to + * increase the hold_multiplier, if it's not already at/past + * maximum value, and wasn't already increased. + */ + if (ht < ospf->spf_max_holdtime) + ospf->spf_hold_multiplier++; + + /* always honour the SPF initial delay */ + if ((ht - elapsed) < ospf->spf_delay) + delay = ospf->spf_delay; + else + delay = ht - elapsed; + } else { + /* Event is past required hold-time of last SPF */ + delay = ospf->spf_delay; + ospf->spf_hold_multiplier = 1; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("SPF: calculation timer delay = %ld msec", delay); + + ospf->t_spf_calc = NULL; + event_add_timer_msec(master, ospf_spf_calculate_schedule_worker, ospf, + delay, &ospf->t_spf_calc); +} + +/* Restart OSPF SPF algorithm*/ +void ospf_restart_spf(struct ospf *ospf) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Restart SPF.", __func__); + + /* Handling inter area and intra area routes*/ + if (ospf->new_table) { + ospf_route_delete(ospf, ospf->new_table); + ospf_route_table_free(ospf->new_table); + ospf->new_table = route_table_init(); + } + + /* Handling of TYPE-5 lsa(external routes) */ + if (ospf->old_external_route) { + ospf_route_delete(ospf, ospf->old_external_route); + ospf_route_table_free(ospf->old_external_route); + ospf->old_external_route = route_table_init(); + } + + /* Trigger SPF */ + ospf_spf_calculate_schedule(ospf, SPF_FLAG_CONFIG_CHANGE); +} diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h new file mode 100644 index 0000000..0837748 --- /dev/null +++ b/ospfd/ospf_spf.h @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF calculation. + * Copyright (C) 1999 Kunihiro Ishiguro + */ + +#ifndef _QUAGGA_OSPF_SPF_H +#define _QUAGGA_OSPF_SPF_H + +#include "typesafe.h" + +/* values for vertex->type */ +#define OSPF_VERTEX_ROUTER 1 /* for a Router-LSA */ +#define OSPF_VERTEX_NETWORK 2 /* for a Network-LSA */ + +/* values for vertex->flags */ +#define OSPF_VERTEX_PROCESSED 0x01 + +/* The "root" is the node running the SPF calculation */ + +PREDECL_SKIPLIST_NONUNIQ(vertex_pqueue); +/* A router or network in an area */ +struct vertex { + struct vertex_pqueue_item pqi; + uint8_t flags; + uint8_t type; /* copied from LSA header */ + struct in_addr id; /* copied from LSA header */ + struct ospf_lsa *lsa_p; + struct lsa_header *lsa; /* Router or Network LSA */ + uint32_t distance; /* from root to this vertex */ + struct list *parents; /* list of parents in SPF tree */ + struct list *children; /* list of children in SPF tree*/ +}; + +struct vertex_nexthop { + struct in_addr router; /* router address to send to */ + int lsa_pos; /* LSA position for resolving the interface */ +}; + +struct vertex_parent { + struct vertex_nexthop *nexthop; /* nexthop taken on the root node */ + struct vertex_nexthop *local_nexthop; /* local nexthop of the parent */ + struct vertex *parent; /* parent vertex */ + int backlink; /* index back to parent for router-lsa's */ +}; + +/* What triggered the SPF ? */ +typedef enum { + SPF_FLAG_ROUTER_LSA_INSTALL = 1, + SPF_FLAG_NETWORK_LSA_INSTALL, + SPF_FLAG_SUMMARY_LSA_INSTALL, + SPF_FLAG_ASBR_SUMMARY_LSA_INSTALL, + SPF_FLAG_MAXAGE, + SPF_FLAG_ABR_STATUS_CHANGE, + SPF_FLAG_ASBR_STATUS_CHANGE, + SPF_FLAG_CONFIG_CHANGE, + SPF_FLAG_GR_FINISH, +} ospf_spf_reason_t; + +extern void ospf_spf_calculate_schedule(struct ospf *, ospf_spf_reason_t); +extern void ospf_spf_calculate(struct ospf_area *area, + struct ospf_lsa *root_lsa, + struct route_table *new_table, + struct route_table *all_rtrs, + struct route_table *new_rtrs, bool is_dry_run, + bool is_root_node); +extern void ospf_spf_calculate_area(struct ospf *ospf, struct ospf_area *area, + struct route_table *new_table, + struct route_table *all_rtrs, + struct route_table *new_rtrs); +extern void ospf_spf_calculate_areas(struct ospf *ospf, + struct route_table *new_table, + struct route_table *all_rtrs, + struct route_table *new_rtrs); +extern void ospf_rtrs_free(struct route_table *); +extern void ospf_spf_cleanup(struct vertex *spf, struct list *vertex_list); +extern void ospf_spf_copy(struct vertex *vertex, struct list *vertex_list); +extern void ospf_spf_remove_resource(struct vertex *vertex, + struct list *vertex_list, + struct protected_resource *resource); +extern struct vertex *ospf_spf_vertex_find(struct in_addr id, + struct list *vertex_list); +extern struct vertex *ospf_spf_vertex_by_nexthop(struct vertex *root, + struct in_addr *nexthop); +extern struct vertex_parent *ospf_spf_vertex_parent_find(struct in_addr id, + struct vertex *vertex); +extern int vertex_parent_cmp(void *aa, void *bb); + +extern void ospf_spf_print(struct vty *vty, struct vertex *v, int i); +extern void ospf_restart_spf(struct ospf *ospf); +/* void ospf_spf_calculate_timer_add (); */ +#endif /* _QUAGGA_OSPF_SPF_H */ diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c new file mode 100644 index 0000000..467cb05 --- /dev/null +++ b/ospfd/ospf_sr.c @@ -0,0 +1,2961 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This is an implementation of Segment Routing + * as per RFC 8665 - OSPF Extensions for Segment Routing + * and RFC 8476 - Signaling Maximum SID Depth (MSD) Using OSPF + * + * Module name: Segment Routing + * + * Author: Olivier Dugeon + * Author: Anselme Sawadogo + * + * Copyright (C) 2016 - 2020 Orange Labs http://www.orange.com + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include "printfrr.h" +#include "command.h" +#include "hash.h" +#include "if.h" +#include "if.h" +#include "jhash.h" +#include "libospf.h" /* for ospf interface types */ +#include "linklist.h" +#include "log.h" +#include "memory.h" +#include "monotime.h" +#include "network.h" +#include "prefix.h" +#include "sockunion.h" /* for inet_aton() */ +#include "stream.h" +#include "table.h" +#include "frrevent.h" +#include "vty.h" +#include "zclient.h" +#include "sbuf.h" +#include +#include "ospf_errors.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_sr.h" +#include "ospfd/ospf_ri.h" +#include "ospfd/ospf_ext.h" +#include "ospfd/ospf_zebra.h" + +/* + * Global variable to manage Segment Routing on this node. + * Note that all parameter values are stored in network byte order. + */ +static struct ospf_sr_db OspfSR; +static void ospf_sr_register_vty(void); +static inline void del_adj_sid(struct sr_nhlfe nhlfe); +static int ospf_sr_start(struct ospf *ospf); + +/* + * Segment Routing Data Base functions + */ + +/* Hash function for Segment Routing entry */ +static unsigned int sr_hash(const void *p) +{ + const struct in_addr *rid = p; + + return jhash_1word(rid->s_addr, 0); +} + +/* Compare 2 Router ID hash entries based on SR Node */ +static bool sr_cmp(const void *p1, const void *p2) +{ + const struct sr_node *srn = p1; + const struct in_addr *rid = p2; + + return IPV4_ADDR_SAME(&srn->adv_router, rid); +} + +/* Functions to remove an SR Link */ +static void del_sr_link(void *val) +{ + struct sr_link *srl = (struct sr_link *)val; + + del_adj_sid(srl->nhlfe[0]); + del_adj_sid(srl->nhlfe[1]); + XFREE(MTYPE_OSPF_SR_PARAMS, val); +} + +/* Functions to remove an SR Prefix */ +static void del_sr_pref(void *val) +{ + struct sr_prefix *srp = (struct sr_prefix *)val; + + ospf_zebra_delete_prefix_sid(srp); + XFREE(MTYPE_OSPF_SR_PARAMS, val); +} + +/* Allocate new Segment Routine node */ +static struct sr_node *sr_node_new(struct in_addr *rid) +{ + + if (rid == NULL) + return NULL; + + struct sr_node *new; + + /* Allocate Segment Routing node memory */ + new = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_node)); + + /* Default Algorithm, SRGB and MSD */ + for (int i = 0; i < ALGORITHM_COUNT; i++) + new->algo[i] = SR_ALGORITHM_UNSET; + + new->srgb.range_size = 0; + new->srgb.lower_bound = 0; + new->msd = 0; + + /* Create Link, Prefix and Range TLVs list */ + new->ext_link = list_new(); + new->ext_prefix = list_new(); + new->ext_link->del = del_sr_link; + new->ext_prefix->del = del_sr_pref; + + IPV4_ADDR_COPY(&new->adv_router, rid); + new->neighbor = NULL; + new->instance = 0; + + osr_debug(" |- Created new SR node for %pI4", &new->adv_router); + return new; +} + +/* Supposed to be used for testing */ +struct sr_node *ospf_sr_node_create(struct in_addr *rid) +{ + struct sr_node *srn; + + srn = hash_get(OspfSR.neighbors, (void *)rid, (void *)sr_node_new); + + return srn; +} + +/* Delete Segment Routing node */ +static void sr_node_del(struct sr_node *srn) +{ + /* Sanity Check */ + if (srn == NULL) + return; + + osr_debug(" |- Delete SR node for %pI4", &srn->adv_router); + + /* Clean Extended Link */ + list_delete(&srn->ext_link); + + /* Clean Prefix List */ + list_delete(&srn->ext_prefix); + + XFREE(MTYPE_OSPF_SR_PARAMS, srn); +} + +/* Get SR Node for a given nexthop */ +static struct sr_node *get_sr_node_by_nexthop(struct ospf *ospf, + struct in_addr nexthop) +{ + struct ospf_interface *oi = NULL; + struct ospf_neighbor *nbr = NULL; + struct listnode *node; + struct route_node *rn; + struct sr_node *srn; + bool found; + + /* Sanity check */ + if (OspfSR.neighbors == NULL) + return NULL; + + osr_debug(" |- Search SR-Node for nexthop %pI4", &nexthop); + + /* First, search neighbor Router ID for this nexthop */ + found = false; + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + nbr = rn->info; + if ((nbr) && (IPV4_ADDR_SAME(&nexthop, &nbr->src))) { + found = true; + break; + } + } + if (found) + break; + } + + if (!found) + return NULL; + + osr_debug(" |- Found nexthop Router ID %pI4", &nbr->router_id); + + /* Then, search SR Node */ + srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, &nbr->router_id); + + return srn; +} + +/* + * Segment Routing Local Block management functions + */ + +/** + * It is necessary to known which label is already allocated to manage the range + * of SRLB. This is particular useful when an interface flap (goes up / down + * frequently). Here, SR will release and then allocate label for the Adjacency + * for each concerned interface. If we don't care, there is a risk to run out of + * label. + * + * For that purpose, a similar principle as already provided to manage chunk of + * label is proposed. But, here, the label chunk has not a fix range of 64 + * labels that could be easily manage with a single variable of 64 bits size. + * So, used_mark is used as a bit wise to mark label reserved (bit set) or not + * (bit unset). Its size is equal to the number of label of the SRLB range round + * up to 64 bits. + * + * - sr__local_block_init() computes the number of 64 bits variables that are + * needed to manage the SRLB range and allocates this number. + * - ospf_sr_local_block_request_label() pick up the first available label and + * set corresponding bit + * - ospf_sr_local_block_release_label() release label by reseting the + * corresponding bit and set the next label to the first free position + */ + +/** + * Initialize Segment Routing Local Block from SRDB configuration and reserve + * block of bits to manage label allocation. + * + * @param lower_bound The lower bound of the SRLB range + * @param upper_bound The upper bound of the SRLB range + * + * @return 0 on success, -1 otherwise + */ +static int sr_local_block_init(uint32_t lower_bound, uint32_t upper_bound) +{ + struct sr_local_block *srlb = &OspfSR.srlb; + uint32_t size; + + /* Check if SRLB is not already configured */ + if (srlb->reserved) + return 0; + + /* + * Request SRLB to the label manager. If the allocation fails, return + * an error to disable SR until a new SRLB is successfully allocated. + */ + size = upper_bound - lower_bound + 1; + if (ospf_zebra_request_label_range(lower_bound, size)) { + zlog_err("SR: Error reserving SRLB [%u/%u] %u labels", + lower_bound, upper_bound, size); + return -1; + } + + osr_debug("SR: Got new SRLB [%u/%u], %u labels", lower_bound, + upper_bound, size); + + /* Initialize the SRLB */ + srlb->start = lower_bound; + srlb->end = upper_bound; + srlb->current = 0; + + /* Compute the needed Used Mark number and allocate them */ + srlb->max_block = size / SRLB_BLOCK_SIZE; + if ((size % SRLB_BLOCK_SIZE) != 0) + srlb->max_block++; + srlb->used_mark = XCALLOC(MTYPE_OSPF_SR_PARAMS, + srlb->max_block * SRLB_BLOCK_SIZE); + srlb->reserved = true; + + return 0; +} + +static int sr_global_block_init(uint32_t start, uint32_t size) +{ + struct sr_global_block *srgb = &OspfSR.srgb; + + /* Check if already configured */ + if (srgb->reserved) + return 0; + + /* request chunk */ + uint32_t end = start + size - 1; + if (ospf_zebra_request_label_range(start, size) < 0) { + zlog_err("SR: Error reserving SRGB [%u/%u], %u labels", start, + end, size); + return -1; + } + + osr_debug("SR: Got new SRGB [%u/%u], %u labels", start, end, size); + + /* success */ + srgb->start = start; + srgb->size = size; + srgb->reserved = true; + return 0; +} + +/** + * Remove Segment Routing Local Block. + * + */ +static void sr_local_block_delete(void) +{ + struct sr_local_block *srlb = &OspfSR.srlb; + + /* Check if SRLB is not already delete */ + if (!srlb->reserved) + return; + + osr_debug("SR (%s): Remove SRLB [%u/%u]", __func__, srlb->start, + srlb->end); + + /* First release the label block */ + ospf_zebra_release_label_range(srlb->start, srlb->end); + + /* Then reset SRLB structure */ + if (srlb->used_mark != NULL) + XFREE(MTYPE_OSPF_SR_PARAMS, srlb->used_mark); + + srlb->reserved = false; +} + +/** + * Remove Segment Routing Global block + */ +static void sr_global_block_delete(void) +{ + struct sr_global_block *srgb = &OspfSR.srgb; + + if (!srgb->reserved) + return; + + osr_debug("SR (%s): Remove SRGB [%u/%u]", __func__, srgb->start, + srgb->start + srgb->size - 1); + + ospf_zebra_release_label_range(srgb->start, + srgb->start + srgb->size - 1); + + srgb->reserved = false; +} + + +/** + * Request a label from the Segment Routing Local Block. + * + * @return First available label on success or MPLS_INVALID_LABEL if the + * block of labels is full + */ +mpls_label_t ospf_sr_local_block_request_label(void) +{ + struct sr_local_block *srlb = &OspfSR.srlb; + mpls_label_t label; + uint32_t index; + uint32_t pos; + uint32_t size = srlb->end - srlb->start + 1; + + /* Check if we ran out of available labels */ + if (srlb->current >= size) + return MPLS_INVALID_LABEL; + + /* Get first available label and mark it used */ + label = srlb->current + srlb->start; + index = srlb->current / SRLB_BLOCK_SIZE; + pos = 1ULL << (srlb->current % SRLB_BLOCK_SIZE); + srlb->used_mark[index] |= pos; + + /* Jump to the next free position */ + srlb->current++; + pos = srlb->current % SRLB_BLOCK_SIZE; + while (srlb->current < size) { + if (pos == 0) + index++; + if (!((1ULL << pos) & srlb->used_mark[index])) + break; + else { + srlb->current++; + pos = srlb->current % SRLB_BLOCK_SIZE; + } + } + + if (srlb->current == size) + zlog_warn( + "SR: Warning, SRLB is depleted and next label request will fail"); + + return label; +} + +/** + * Release label in the Segment Routing Local Block. + * + * @param label Label to be release + * + * @return 0 on success or -1 if label falls outside SRLB + */ +int ospf_sr_local_block_release_label(mpls_label_t label) +{ + struct sr_local_block *srlb = &OspfSR.srlb; + uint32_t index; + uint32_t pos; + + /* Check that label falls inside the SRLB */ + if ((label < srlb->start) || (label > srlb->end)) { + flog_warn(EC_OSPF_SR_SID_OVERFLOW, + "%s: Returning label %u is outside SRLB [%u/%u]", + __func__, label, srlb->start, srlb->end); + return -1; + } + + index = (label - srlb->start) / SRLB_BLOCK_SIZE; + pos = 1ULL << ((label - srlb->start) % SRLB_BLOCK_SIZE); + srlb->used_mark[index] &= ~pos; + /* Reset current to the first available position */ + for (index = 0; index < srlb->max_block; index++) { + if (srlb->used_mark[index] != 0xFFFFFFFFFFFFFFFF) { + for (pos = 0; pos < SRLB_BLOCK_SIZE; pos++) + if (!((1ULL << pos) & srlb->used_mark[index])) { + srlb->current = + index * SRLB_BLOCK_SIZE + pos; + break; + } + break; + } + } + + return 0; +} + +/* + * Segment Routing Initialization functions + */ + +/** + * Thread function to re-attempt connection to the Label Manager and thus be + * able to start Segment Routing. + * + * @param start Thread structure that contains area as argument + * + * @return 1 on success + */ +static void sr_start_label_manager(struct event *start) +{ + struct ospf *ospf; + + ospf = EVENT_ARG(start); + + /* re-attempt to start SR & Label Manager connection */ + ospf_sr_start(ospf); +} + +/* Segment Routing starter function */ +static int ospf_sr_start(struct ospf *ospf) +{ + struct route_node *rn; + struct ospf_lsa *lsa; + struct sr_node *srn; + int rc = 0; + + osr_debug("SR (%s): Start Segment Routing", __func__); + + /* Initialize self SR Node if not already done */ + if (OspfSR.self == NULL) { + srn = hash_get(OspfSR.neighbors, (void *)&(ospf->router_id), + (void *)sr_node_new); + + /* Complete & Store self SR Node */ + srn->srgb.range_size = OspfSR.srgb.size; + srn->srgb.lower_bound = OspfSR.srgb.start; + srn->srlb.lower_bound = OspfSR.srlb.start; + srn->srlb.range_size = OspfSR.srlb.end - OspfSR.srlb.start + 1; + srn->algo[0] = OspfSR.algo[0]; + srn->msd = OspfSR.msd; + OspfSR.self = srn; + } + + /* Then, start Label Manager if not ready */ + if (!ospf_zebra_label_manager_ready()) + if (ospf_zebra_label_manager_connect() < 0) { + /* Re-attempt to connect to Label Manager in 1 sec. */ + event_add_timer(master, sr_start_label_manager, ospf, 1, + &OspfSR.t_start_lm); + osr_debug(" |- Failed to start the Label Manager"); + return -1; + } + + /* + * Request SRLB & SGRB to the label manager if not already reserved. + * If the allocation fails, return an error to disable SR until a new + * SRLB and/or SRGB are successfully allocated. + */ + if (sr_local_block_init(OspfSR.srlb.start, OspfSR.srlb.end) < 0) + return -1; + + if (sr_global_block_init(OspfSR.srgb.start, OspfSR.srgb.size) < 0) + return -1; + + /* SR is UP and ready to flood LSA */ + OspfSR.status = SR_UP; + + /* Set Router Information SR parameters */ + osr_debug("SR: Activate SR for Router Information LSA"); + + ospf_router_info_update_sr(true, OspfSR.self); + + /* Update Ext LSA */ + osr_debug("SR: Activate SR for Extended Link/Prefix LSA"); + + ospf_ext_update_sr(true); + + osr_debug("SR (%s): Update SR-DB from LSDB", __func__); + + /* Start by looking to Router Info & Extended LSA in lsdb */ + if ((ospf != NULL) && (ospf->backbone != NULL)) { + LSDB_LOOP (OPAQUE_AREA_LSDB(ospf->backbone), rn, lsa) { + if (IS_LSA_MAXAGE(lsa) || IS_LSA_SELF(lsa)) + continue; + int lsa_id = + GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)); + switch (lsa_id) { + case OPAQUE_TYPE_ROUTER_INFORMATION_LSA: + ospf_sr_ri_lsa_update(lsa); + break; + case OPAQUE_TYPE_EXTENDED_PREFIX_LSA: + ospf_sr_ext_prefix_lsa_update(lsa); + break; + case OPAQUE_TYPE_EXTENDED_LINK_LSA: + ospf_sr_ext_link_lsa_update(lsa); + break; + default: + break; + } + } + } + + rc = 1; + return rc; +} + +/* Stop Segment Routing */ +static void ospf_sr_stop(void) +{ + + if (OspfSR.status == SR_OFF) + return; + + osr_debug("SR (%s): Stop Segment Routing", __func__); + + /* Disable any re-attempt to connect to Label Manager */ + EVENT_OFF(OspfSR.t_start_lm); + + /* Release SRGB if active */ + sr_global_block_delete(); + + /* Release SRLB if active */ + sr_local_block_delete(); + + /* + * Remove all SR Nodes from the Hash table. Prefix and Link SID will + * be remove though list_delete() call. See sr_node_del() + */ + hash_clean(OspfSR.neighbors, (void *)sr_node_del); + OspfSR.self = NULL; + OspfSR.status = SR_OFF; +} + +/* + * Segment Routing initialize function + * + * @param - nothing + * + * @return 0 if OK, -1 otherwise + */ +int ospf_sr_init(void) +{ + int rc = -1; + + osr_debug("SR (%s): Initialize SR Data Base", __func__); + + memset(&OspfSR, 0, sizeof(OspfSR)); + OspfSR.status = SR_OFF; + /* Only AREA flooding is supported in this release */ + OspfSR.scope = OSPF_OPAQUE_AREA_LSA; + + /* Initialize Algorithms, SRGB, SRLB and MSD TLVs */ + /* Only Algorithm SPF is supported */ + OspfSR.algo[0] = SR_ALGORITHM_SPF; + for (int i = 1; i < ALGORITHM_COUNT; i++) + OspfSR.algo[i] = SR_ALGORITHM_UNSET; + + OspfSR.srgb.size = DEFAULT_SRGB_SIZE; + OspfSR.srgb.start = DEFAULT_SRGB_LABEL; + OspfSR.srgb.reserved = false; + + OspfSR.srlb.start = DEFAULT_SRLB_LABEL; + OspfSR.srlb.end = DEFAULT_SRLB_END; + OspfSR.srlb.reserved = false; + OspfSR.msd = 0; + + /* Initialize Hash table for neighbor SR nodes */ + OspfSR.neighbors = hash_create(sr_hash, sr_cmp, "OSPF_SR"); + if (OspfSR.neighbors == NULL) + return rc; + + /* Register Segment Routing VTY command */ + ospf_sr_register_vty(); + + rc = 0; + return rc; +} + +/* + * Segment Routing termination function + * + * @param - nothing + * @return - nothing + */ +void ospf_sr_term(void) +{ + + /* Stop Segment Routing */ + ospf_sr_stop(); + + hash_clean_and_free(&OspfSR.neighbors, (void *)sr_node_del); +} + +/* + * Segment Routing finish function + * + * @param - nothing + * @return - nothing + */ +void ospf_sr_finish(void) +{ + /* Stop Segment Routing */ + ospf_sr_stop(); +} + +/* + * Following functions are used to manipulate the + * Next Hop Label Forwarding entry (NHLFE) + */ + +/* Compute label from index */ +static mpls_label_t index2label(uint32_t index, struct sr_block srgb) +{ + mpls_label_t label; + + label = srgb.lower_bound + index; + if (label > (srgb.lower_bound + srgb.range_size)) { + flog_warn(EC_OSPF_SR_SID_OVERFLOW, + "%s: SID index %u falls outside SRGB range", + __func__, index); + return MPLS_INVALID_LABEL; + } else + return label; +} + +/* Get the prefix sid for a specific router id */ +mpls_label_t ospf_sr_get_prefix_sid_by_id(struct in_addr *id) +{ + struct sr_node *srn; + struct sr_prefix *srp; + mpls_label_t label; + + srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, id); + + if (srn) { + /* + * TODO: Here we assume that the SRGBs are the same, + * and that the node's prefix SID is at the head of + * the list, probably needs tweaking. + */ + srp = listnode_head(srn->ext_prefix); + label = index2label(srp->sid, srn->srgb); + } else { + label = MPLS_INVALID_LABEL; + } + + return label; +} + +/* Get the adjacency sid for a specific 'root' id and 'neighbor' id */ +mpls_label_t ospf_sr_get_adj_sid_by_id(struct in_addr *root_id, + struct in_addr *neighbor_id) +{ + struct sr_node *srn; + struct sr_link *srl; + mpls_label_t label; + struct listnode *node; + + srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, root_id); + + label = MPLS_INVALID_LABEL; + + if (srn) { + for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) { + if (srl->type == ADJ_SID + && srl->remote_id.s_addr == neighbor_id->s_addr) { + label = srl->sid[0]; + break; + } + } + } + + return label; +} + +/* Get neighbor full structure from address */ +static struct ospf_neighbor *get_neighbor_by_addr(struct ospf *top, + struct in_addr addr) +{ + struct ospf_neighbor *nbr; + struct ospf_interface *oi; + struct listnode *node; + struct route_node *rn; + + /* Sanity Check */ + if (top == NULL) + return NULL; + + for (ALL_LIST_ELEMENTS_RO(top->oiflist, node, oi)) + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + nbr = rn->info; + if (!nbr) + continue; + + if (IPV4_ADDR_SAME(&nbr->address.u.prefix4, &addr) || + IPV4_ADDR_SAME(&nbr->router_id, &addr)) { + route_unlock_node(rn); + return nbr; + } + } + return NULL; +} + +/* Get OSPF Path from address */ +static struct ospf_route *get_nexthop_by_addr(struct ospf *top, + struct prefix_ipv4 p) +{ + struct route_node *rn; + + /* Sanity Check */ + if (top == NULL) + return NULL; + + osr_debug(" |- Search Nexthop for prefix %pFX", + (struct prefix *)&p); + + rn = route_node_lookup(top->new_table, (struct prefix *)&p); + + /* + * Check if we found an OSPF route. May be NULL if SPF has not + * yet populate routing table for this prefix. + */ + if (rn == NULL) + return NULL; + + route_unlock_node(rn); + return rn->info; +} + +/* Compute NHLFE entry for Extended Link */ +static int compute_link_nhlfe(struct sr_link *srl) +{ + struct ospf *top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + struct ospf_neighbor *nh; + int rc = 0; + + osr_debug(" |- Compute NHLFE for link %pI4", &srl->itf_addr); + + /* First determine the OSPF Neighbor */ + nh = get_neighbor_by_addr(top, srl->nhlfe[0].nexthop); + + /* Neighbor could be not found when OSPF Adjacency just fire up + * because SPF don't yet populate routing table. This NHLFE will + * be fixed later when SR SPF schedule will be called. + */ + if (nh == NULL) + return rc; + + osr_debug(" |- Found nexthop %pI4", &nh->router_id); + + /* Set ifindex for this neighbor */ + srl->nhlfe[0].ifindex = nh->oi->ifp->ifindex; + srl->nhlfe[1].ifindex = nh->oi->ifp->ifindex; + + /* Update neighbor address for LAN_ADJ_SID */ + if (srl->type == LAN_ADJ_SID) { + IPV4_ADDR_COPY(&srl->nhlfe[0].nexthop, &nh->src); + IPV4_ADDR_COPY(&srl->nhlfe[1].nexthop, &nh->src); + } + + /* Set Input & Output Label */ + if (CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + srl->nhlfe[0].label_in = srl->sid[0]; + else + srl->nhlfe[0].label_in = + index2label(srl->sid[0], srl->srn->srgb); + if (CHECK_FLAG(srl->flags[1], EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + srl->nhlfe[1].label_in = srl->sid[1]; + else + srl->nhlfe[1].label_in = + index2label(srl->sid[1], srl->srn->srgb); + + srl->nhlfe[0].label_out = MPLS_LABEL_IMPLICIT_NULL; + srl->nhlfe[1].label_out = MPLS_LABEL_IMPLICIT_NULL; + + rc = 1; + return rc; +} + +/** + * Compute output label for the given Prefix-SID. + * + * @param srp Segment Routing Prefix + * @param srnext Segment Routing nexthop node + * + * @return MPLS label or MPLS_INVALID_LABEL in case of error + */ +static mpls_label_t sr_prefix_out_label(const struct sr_prefix *srp, + const struct sr_node *srnext) +{ + /* Check if the nexthop SR Node is the last hop? */ + if (srnext == srp->srn) { + /* SR-Node doesn't request NO-PHP. Return Implicit NULL label */ + if (!CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)) + return MPLS_LABEL_IMPLICIT_NULL; + + /* SR-Node requests Explicit NULL Label */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_EFLG)) + return MPLS_LABEL_IPV4_EXPLICIT_NULL; + /* Fallthrough */ + } + + /* Return SID value as MPLS label if it is an Absolute SID */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG + | EXT_SUBTLV_PREFIX_SID_LFLG)) { + /* + * V/L SIDs have local significance, so only adjacent routers + * can use them (RFC8665 section #5) + */ + if (srp->srn != srnext) + return MPLS_INVALID_LABEL; + return srp->sid; + } + + /* Return MPLS label as SRGB lower bound + SID index as per RFC 8665 */ + return (index2label(srp->sid, srnext->srgb)); +} + +/* + * Compute NHLFE entry for Extended Prefix + * + * @param srp - Segment Routing Prefix + * + * @return -1 if no route is found, 0 if there is no SR route ready + * and 1 if success or update + */ +static int compute_prefix_nhlfe(struct sr_prefix *srp) +{ + struct ospf *top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + struct ospf_path *path; + struct listnode *node; + struct sr_node *srnext; + int rc = -1; + + osr_debug(" |- Compute NHLFE for prefix %pFX", + (struct prefix *)&srp->prefv4); + + + /* First determine the nexthop */ + srp->route = get_nexthop_by_addr(top, srp->prefv4); + + /* Nexthop could be not found when OSPF Adjacency just fire up + * because SPF don't yet populate routing table. This NHLFE will + * be fixed later when SR SPF schedule will be called. + */ + if (srp->route == NULL) + return rc; + + /* Compute Input Label with self SRGB */ + srp->label_in = index2label(srp->sid, OspfSR.self->srgb); + + rc = 0; + for (ALL_LIST_ELEMENTS_RO(srp->route->paths, node, path)) { + + osr_debug(" |- Process new route via %pI4 for this prefix", + &path->nexthop); + + /* + * Get SR-Node for this nexthop. Could be not yet available + * as Extended Link / Prefix and Router Information are flooded + * after LSA Type 1 & 2 which populate the OSPF Route Table + */ + srnext = get_sr_node_by_nexthop(top, path->nexthop); + if (srnext == NULL) + continue; + + /* And store this information for later update */ + srnext->neighbor = OspfSR.self; + path->srni.nexthop = srnext; + + /* + * SR Node could be known, but SRGB could be not initialize + * This is due to the fact that Extended Link / Prefix could + * be received before corresponding Router Information LSA + */ + if (srnext == NULL || srnext->srgb.lower_bound == 0 + || srnext->srgb.range_size == 0) { + osr_debug( + " |- SR-Node %pI4 not ready. Stop process", + &srnext->adv_router); + path->srni.label_out = MPLS_INVALID_LABEL; + continue; + } + + osr_debug(" |- Found SRGB %u/%u for next hop SR-Node %pI4", + srnext->srgb.range_size, srnext->srgb.lower_bound, + &srnext->adv_router); + + /* Compute Output Label with Nexthop SR Node SRGB */ + path->srni.label_out = sr_prefix_out_label(srp, srnext); + + osr_debug(" |- Computed new labels in: %u out: %u", + srp->label_in, path->srni.label_out); + rc = 1; + } + return rc; +} + +/* Add new NHLFE entry for Adjacency SID */ +static inline void add_adj_sid(struct sr_nhlfe nhlfe) +{ + if (nhlfe.label_in != 0) + ospf_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_ADD, nhlfe); +} + +/* Remove NHLFE entry for Adjacency SID */ +static inline void del_adj_sid(struct sr_nhlfe nhlfe) +{ + if (nhlfe.label_in != 0) + ospf_zebra_send_adjacency_sid(ZEBRA_MPLS_LABELS_DELETE, nhlfe); +} + +/* Update NHLFE entry for Adjacency SID */ +static inline void update_adj_sid(struct sr_nhlfe n1, struct sr_nhlfe n2) +{ + del_adj_sid(n1); + add_adj_sid(n2); +} + +/* + * Functions to parse and get Extended Link / Prefix + * TLVs and SubTLVs + */ + +/* Extended Link SubTLVs Getter */ +static struct sr_link *get_ext_link_sid(struct tlv_header *tlvh, size_t size) +{ + + struct sr_link *srl; + struct ext_tlv_link *link = (struct ext_tlv_link *)tlvh; + struct ext_subtlv_adj_sid *adj_sid; + struct ext_subtlv_lan_adj_sid *lan_sid; + struct ext_subtlv_rmt_itf_addr *rmt_itf; + + struct tlv_header *sub_tlvh; + uint16_t length = 0, sum = 0, i = 0; + + /* Check TLV size */ + if ((ntohs(tlvh->length) > size) + || ntohs(tlvh->length) < EXT_TLV_LINK_SIZE) { + zlog_warn("Wrong Extended Link TLV size. Abort!"); + return NULL; + } + + srl = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_link)); + + /* Initialize TLV browsing */ + length = ntohs(tlvh->length) - EXT_TLV_LINK_SIZE; + sub_tlvh = (struct tlv_header *)((char *)(tlvh) + TLV_HDR_SIZE + + EXT_TLV_LINK_SIZE); + for (; sum < length && sub_tlvh; sub_tlvh = TLV_HDR_NEXT(sub_tlvh)) { + switch (ntohs(sub_tlvh->type)) { + case EXT_SUBTLV_ADJ_SID: + adj_sid = (struct ext_subtlv_adj_sid *)sub_tlvh; + srl->type = ADJ_SID; + i = CHECK_FLAG(adj_sid->flags, + EXT_SUBTLV_LINK_ADJ_SID_BFLG) + ? 1 + : 0; + srl->flags[i] = adj_sid->flags; + if (CHECK_FLAG(adj_sid->flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + srl->sid[i] = GET_LABEL(ntohl(adj_sid->value)); + else + srl->sid[i] = ntohl(adj_sid->value); + IPV4_ADDR_COPY(&srl->nhlfe[i].nexthop, &link->link_id); + break; + case EXT_SUBTLV_LAN_ADJ_SID: + lan_sid = (struct ext_subtlv_lan_adj_sid *)sub_tlvh; + srl->type = LAN_ADJ_SID; + i = CHECK_FLAG(lan_sid->flags, + EXT_SUBTLV_LINK_ADJ_SID_BFLG) + ? 1 + : 0; + srl->flags[i] = lan_sid->flags; + if (CHECK_FLAG(lan_sid->flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + srl->sid[i] = GET_LABEL(ntohl(lan_sid->value)); + else + srl->sid[i] = ntohl(lan_sid->value); + IPV4_ADDR_COPY(&srl->nhlfe[i].nexthop, + &lan_sid->neighbor_id); + break; + case EXT_SUBTLV_RMT_ITF_ADDR: + rmt_itf = (struct ext_subtlv_rmt_itf_addr *)sub_tlvh; + IPV4_ADDR_COPY(&srl->nhlfe[0].nexthop, &rmt_itf->value); + IPV4_ADDR_COPY(&srl->nhlfe[1].nexthop, &rmt_itf->value); + break; + default: + break; + } + sum += TLV_SIZE(sub_tlvh); + } + + IPV4_ADDR_COPY(&srl->itf_addr, &link->link_data); + + osr_debug(" |- Found primary %u and backup %u Adj/Lan Sid for %pI4", + srl->sid[0], srl->sid[1], &srl->itf_addr); + + return srl; +} + +/* Extended Prefix SubTLVs Getter */ +static struct sr_prefix *get_ext_prefix_sid(struct tlv_header *tlvh, + size_t size) +{ + + struct sr_prefix *srp; + struct ext_tlv_prefix *pref = (struct ext_tlv_prefix *)tlvh; + struct ext_subtlv_prefix_sid *psid; + + struct tlv_header *sub_tlvh; + uint16_t length = 0, sum = 0; + + /* Check TLV size */ + if ((ntohs(tlvh->length) > size) + || ntohs(tlvh->length) < EXT_TLV_PREFIX_SIZE) { + zlog_warn("Wrong Extended Link TLV size. Abort!"); + return NULL; + } + + srp = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix)); + + /* Initialize TLV browsing */ + length = ntohs(tlvh->length) - EXT_TLV_PREFIX_SIZE; + sub_tlvh = (struct tlv_header *)((char *)(tlvh) + TLV_HDR_SIZE + + EXT_TLV_PREFIX_SIZE); + for (; sum < length && sub_tlvh; sub_tlvh = TLV_HDR_NEXT(sub_tlvh)) { + switch (ntohs(sub_tlvh->type)) { + case EXT_SUBTLV_PREFIX_SID: + psid = (struct ext_subtlv_prefix_sid *)sub_tlvh; + if (psid->algorithm != SR_ALGORITHM_SPF) { + flog_err(EC_OSPF_INVALID_ALGORITHM, + "SR (%s): Unsupported Algorithm", + __func__); + XFREE(MTYPE_OSPF_SR_PARAMS, srp); + return NULL; + } + srp->type = PREF_SID; + srp->flags = psid->flags; + if (CHECK_FLAG(psid->flags, EXT_SUBTLV_PREFIX_SID_VFLG)) + srp->sid = GET_LABEL(ntohl(psid->value)); + else + srp->sid = ntohl(psid->value); + IPV4_ADDR_COPY(&srp->prefv4.prefix, &pref->address); + srp->prefv4.prefixlen = pref->pref_length; + srp->prefv4.family = AF_INET; + apply_mask_ipv4(&srp->prefv4); + break; + default: + break; + } + sum += TLV_SIZE(sub_tlvh); + } + + osr_debug(" |- Found SID %u for prefix %pFX", srp->sid, + (struct prefix *)&srp->prefv4); + + return srp; +} + +/* + * Functions to manipulate Segment Routing Link & Prefix structures + */ + +/* Compare two Segment Link: return 0 if equal, 1 otherwise */ +static inline int sr_link_cmp(struct sr_link *srl1, struct sr_link *srl2) +{ + if ((srl1->sid[0] == srl2->sid[0]) && (srl1->sid[1] == srl2->sid[1]) + && (srl1->type == srl2->type) && (srl1->flags[0] == srl2->flags[0]) + && (srl1->flags[1] == srl2->flags[1])) + return 0; + else + return 1; +} + +/* Compare two Segment Prefix: return 0 if equal, 1 otherwise */ +static inline int sr_prefix_cmp(struct sr_prefix *srp1, struct sr_prefix *srp2) +{ + if ((srp1->sid == srp2->sid) && (srp1->flags == srp2->flags)) + return 0; + else + return 1; +} + +/* Update Segment Link of given Segment Routing Node */ +static void update_ext_link_sid(struct sr_node *srn, struct sr_link *srl, + uint8_t lsa_flags) +{ + struct listnode *node; + struct sr_link *lk; + bool found = false; + bool config = true; + + /* Sanity check */ + if ((srn == NULL) || (srl == NULL)) + return; + + osr_debug(" |- Process Extended Link Adj/Lan-SID"); + + /* Detect if Adj/Lan_Adj SID must be configured */ + if (!CHECK_FLAG(lsa_flags, OSPF_LSA_SELF) + && (CHECK_FLAG(srl->flags[0], EXT_SUBTLV_LINK_ADJ_SID_LFLG) + || CHECK_FLAG(srl->flags[1], EXT_SUBTLV_LINK_ADJ_SID_LFLG))) + config = false; + + /* Search for existing Segment Link */ + for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, lk)) + if (lk->instance == srl->instance) { + found = true; + break; + } + + osr_debug(" |- %s SR Link 8.0.0.%u for SR node %pI4", + found ? "Update" : "Add", GET_OPAQUE_ID(srl->instance), + &srn->adv_router); + + /* if not found, add new Segment Link and install NHLFE */ + if (!found) { + /* Complete SR-Link and add it to SR-Node list */ + srl->srn = srn; + IPV4_ADDR_COPY(&srl->adv_router, &srn->adv_router); + listnode_add(srn->ext_link, srl); + /* Try to set MPLS table */ + if (config && compute_link_nhlfe(srl)) { + add_adj_sid(srl->nhlfe[0]); + add_adj_sid(srl->nhlfe[1]); + } + } else { + /* Update SR-Link if they are different */ + if (sr_link_cmp(lk, srl)) { + /* Try to set MPLS table */ + if (config) { + if (compute_link_nhlfe(srl)) { + update_adj_sid(lk->nhlfe[0], + srl->nhlfe[0]); + update_adj_sid(lk->nhlfe[1], + srl->nhlfe[1]); + } else { + del_adj_sid(lk->nhlfe[0]); + del_adj_sid(lk->nhlfe[1]); + } + } + /* Replace SR-Link in SR-Node Adjacency List */ + listnode_delete(srn->ext_link, lk); + XFREE(MTYPE_OSPF_SR_PARAMS, lk); + srl->srn = srn; + IPV4_ADDR_COPY(&srl->adv_router, &srn->adv_router); + listnode_add(srn->ext_link, srl); + } else { + /* + * This is just an LSA refresh. + * Stop processing and free SR Link + */ + XFREE(MTYPE_OSPF_SR_PARAMS, srl); + } + } +} + +/* Update Segment Prefix of given Segment Routing Node */ +static void update_ext_prefix_sid(struct sr_node *srn, struct sr_prefix *srp) +{ + + struct listnode *node; + struct sr_prefix *pref; + bool found = false; + + /* Sanity check */ + if (srn == NULL || srp == NULL) + return; + + osr_debug(" |- Process Extended Prefix SID %u", srp->sid); + + /* Process only Global Prefix SID */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_LFLG)) + return; + + /* Search for existing Segment Prefix */ + for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, pref)) + if (pref->instance == srp->instance + && prefix_same((struct prefix *)&srp->prefv4, + &pref->prefv4)) { + found = true; + break; + } + + osr_debug(" |- %s SR LSA ID 7.0.0.%u for SR node %pI4", + found ? "Update" : "Add", GET_OPAQUE_ID(srp->instance), + &srn->adv_router); + + /* Complete SR-Prefix */ + srp->srn = srn; + IPV4_ADDR_COPY(&srp->adv_router, &srn->adv_router); + + /* if not found, add new Segment Prefix and install NHLFE */ + if (!found) { + /* Add it to SR-Node list ... */ + listnode_add(srn->ext_prefix, srp); + /* ... and try to set MPLS table */ + if (compute_prefix_nhlfe(srp) == 1) + ospf_zebra_update_prefix_sid(srp); + } else { + /* + * An old SR prefix exist. Check if something changes or if it + * is just a refresh. + */ + if (sr_prefix_cmp(pref, srp)) { + if (compute_prefix_nhlfe(srp) == 1) { + ospf_zebra_delete_prefix_sid(pref); + /* Replace Segment Prefix */ + listnode_delete(srn->ext_prefix, pref); + XFREE(MTYPE_OSPF_SR_PARAMS, pref); + listnode_add(srn->ext_prefix, srp); + ospf_zebra_update_prefix_sid(srp); + } else { + /* New NHLFE was not found. + * Just free the SR Prefix + */ + XFREE(MTYPE_OSPF_SR_PARAMS, srp); + } + } else { + /* This is just an LSA refresh. + * Stop processing and free SR Prefix + */ + XFREE(MTYPE_OSPF_SR_PARAMS, srp); + } + } +} + +/* + * When change the FRR Self SRGB, update the NHLFE Input Label + * for all Extended Prefix with SID index through hash_iterate() + */ +static void update_in_nhlfe(struct hash_bucket *bucket, void *args) +{ + struct listnode *node; + struct sr_node *srn = (struct sr_node *)bucket->data; + struct sr_prefix *srp; + + /* Process Every Extended Prefix for this SR-Node */ + for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) { + /* Process Self SRN only if NO-PHP is requested */ + if ((srn == OspfSR.self) + && !CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG)) + continue; + + /* Process only SID Index */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_VFLG)) + continue; + + /* First, remove old MPLS table entries ... */ + ospf_zebra_delete_prefix_sid(srp); + /* ... then compute new input label ... */ + srp->label_in = index2label(srp->sid, OspfSR.self->srgb); + /* ... and install new MPLS LFIB */ + ospf_zebra_update_prefix_sid(srp); + } +} + +/* + * When SRGB has changed, update NHLFE Output Label for all Extended Prefix + * with SID index which use the given SR-Node as nexthop through hash_iterate() + */ +static void update_out_nhlfe(struct hash_bucket *bucket, void *args) +{ + struct listnode *node, *pnode; + struct sr_node *srn = (struct sr_node *)bucket->data; + struct sr_node *srnext = (struct sr_node *)args; + struct sr_prefix *srp; + struct ospf_path *path; + + /* Skip Self SR-Node */ + if (srn == OspfSR.self) + return; + + osr_debug("SR (%s): Update Out NHLFE for neighbor SR-Node %pI4", + __func__, &srn->adv_router); + + for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) { + /* Skip Prefix that has not yet a valid route */ + if (srp->route == NULL) + continue; + + for (ALL_LIST_ELEMENTS_RO(srp->route->paths, pnode, path)) { + /* Skip path that has not next SR-Node as nexthop */ + if (path->srni.nexthop != srnext) + continue; + + /* Compute new Output Label */ + path->srni.label_out = sr_prefix_out_label(srp, srnext); + } + + /* Finally update MPLS table */ + ospf_zebra_update_prefix_sid(srp); + } +} + +/* + * Following functions are call when new Segment Routing LSA are received + * - Router Information: ospf_sr_ri_lsa_update() & ospf_sr_ri_lsa_delete() + * - Extended Link: ospf_sr_ext_link_update() & ospf_sr_ext_link_delete() + * - Extended Prefix: ospf_ext_prefix_update() & ospf_sr_ext_prefix_delete() + */ + +/* Update Segment Routing from Router Information LSA */ +void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa) +{ + struct sr_node *srn; + struct tlv_header *tlvh; + struct lsa_header *lsah = lsa->data; + struct ri_sr_tlv_sid_label_range *ri_srgb = NULL; + struct ri_sr_tlv_sid_label_range *ri_srlb = NULL; + struct ri_sr_tlv_sr_algorithm *algo = NULL; + struct sr_block srgb; + uint16_t length = 0, sum = 0; + uint8_t msd = 0; + + osr_debug("SR (%s): Process Router Information LSA 4.0.0.%u from %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); + + /* Sanity check */ + if (IS_LSA_SELF(lsa)) + return; + + if (OspfSR.neighbors == NULL) { + flog_err(EC_OSPF_SR_INVALID_DB, + "SR (%s): Abort! no valid SR DataBase", __func__); + return; + } + + /* Search SR Node in hash table from Router ID */ + srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, + &lsah->adv_router); + + + /* Collect Router Information Sub TLVs */ + /* Initialize TLV browsing */ + length = lsa->size - OSPF_LSA_HEADER_SIZE; + srgb.range_size = 0; + srgb.lower_bound = 0; + + for (tlvh = TLV_HDR_TOP(lsah); (sum < length) && (tlvh != NULL); + tlvh = TLV_HDR_NEXT(tlvh)) { + switch (ntohs(tlvh->type)) { + case RI_SR_TLV_SR_ALGORITHM: + algo = (struct ri_sr_tlv_sr_algorithm *)tlvh; + break; + case RI_SR_TLV_SRGB_LABEL_RANGE: + ri_srgb = (struct ri_sr_tlv_sid_label_range *)tlvh; + break; + case RI_SR_TLV_SRLB_LABEL_RANGE: + ri_srlb = (struct ri_sr_tlv_sid_label_range *)tlvh; + break; + case RI_SR_TLV_NODE_MSD: + msd = ((struct ri_sr_tlv_node_msd *)(tlvh))->value; + break; + default: + break; + } + sum += TLV_SIZE(tlvh); + } + + /* Check if Segment Routing Capabilities has been found */ + if (ri_srgb == NULL) { + /* Skip Router Information without SR capabilities + * advertise by a non SR Node */ + if (srn == NULL) { + return; + } else { + /* Remove SR Node that advertise Router Information + * without SR capabilities. This could correspond to a + * Node stopping Segment Routing */ + hash_release(OspfSR.neighbors, &(srn->adv_router)); + sr_node_del(srn); + return; + } + } + + /* Check that RI LSA belongs to the correct SR Node */ + if ((srn != NULL) && (srn->instance != 0) + && (srn->instance != ntohl(lsah->id.s_addr))) { + flog_err(EC_OSPF_SR_INVALID_LSA_ID, + "SR (%s): Abort! Wrong LSA ID 4.0.0.%u for SR node %pI4/%u", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router, srn->instance); + return; + } + + /* OK. All things look good. Get SRGB */ + srgb.range_size = GET_RANGE_SIZE(ntohl(ri_srgb->size)); + srgb.lower_bound = GET_LABEL(ntohl(ri_srgb->lower.value)); + + /* Check if it is a new SR Node or not */ + if (srn == NULL) { + /* Get a new SR Node in hash table from Router ID */ + srn = (struct sr_node *)hash_get(OspfSR.neighbors, + &lsah->adv_router, + (void *)sr_node_new); + /* update LSA ID */ + srn->instance = ntohl(lsah->id.s_addr); + /* Copy SRGB */ + srn->srgb.range_size = srgb.range_size; + srn->srgb.lower_bound = srgb.lower_bound; + } + + /* Update Algorithm, SRLB and MSD if present */ + if (algo != NULL) { + int i; + for (i = 0; i < ntohs(algo->header.length); i++) + srn->algo[i] = algo->value[0]; + for (; i < ALGORITHM_COUNT; i++) + srn->algo[i] = SR_ALGORITHM_UNSET; + } else { + srn->algo[0] = SR_ALGORITHM_SPF; + } + srn->msd = msd; + if (ri_srlb != NULL) { + srn->srlb.range_size = GET_RANGE_SIZE(ntohl(ri_srlb->size)); + srn->srlb.lower_bound = GET_LABEL(ntohl(ri_srlb->lower.value)); + } + + /* Check if SRGB has changed */ + if ((srn->srgb.range_size == srgb.range_size) + && (srn->srgb.lower_bound == srgb.lower_bound)) + return; + + /* Copy SRGB */ + srn->srgb.range_size = srgb.range_size; + srn->srgb.lower_bound = srgb.lower_bound; + + osr_debug(" |- Update SR-Node[%pI4], SRGB[%u/%u], SRLB[%u/%u], Algo[%u], MSD[%u]", + &srn->adv_router, srn->srgb.lower_bound, srn->srgb.range_size, + srn->srlb.lower_bound, srn->srlb.range_size, srn->algo[0], + srn->msd); + + /* ... and NHLFE if it is a neighbor SR node */ + if (srn->neighbor == OspfSR.self) + hash_iterate(OspfSR.neighbors, update_out_nhlfe, srn); +} + +/* + * Delete SR Node entry in hash table information corresponding to an expired + * Router Information LSA + */ +void ospf_sr_ri_lsa_delete(struct ospf_lsa *lsa) +{ + struct sr_node *srn; + struct lsa_header *lsah = lsa->data; + + osr_debug("SR (%s): Remove SR node %pI4 from lsa_id 4.0.0.%u", __func__, + &lsah->adv_router, GET_OPAQUE_ID(ntohl(lsah->id.s_addr))); + + /* Sanity check */ + if (OspfSR.neighbors == NULL) { + flog_err(EC_OSPF_SR_INVALID_DB, + "SR (%s): Abort! no valid SR Data Base", __func__); + return; + } + + /* Release Router ID entry in SRDB hash table */ + srn = hash_release(OspfSR.neighbors, &(lsah->adv_router)); + + /* Sanity check */ + if (srn == NULL) { + flog_err(EC_OSPF_SR_NODE_CREATE, + "SR (%s): Abort! no entry in SRDB for SR Node %pI4", + __func__, &lsah->adv_router); + return; + } + + if ((srn->instance != 0) && (srn->instance != ntohl(lsah->id.s_addr))) { + flog_err( + EC_OSPF_SR_INVALID_LSA_ID, + "SR (%s): Abort! Wrong LSA ID 4.0.0.%u for SR node %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); + return; + } + + /* Remove SR node */ + sr_node_del(srn); +} + +/* Update Segment Routing from Extended Link LSA */ +void ospf_sr_ext_link_lsa_update(struct ospf_lsa *lsa) +{ + struct sr_node *srn; + struct tlv_header *tlvh; + struct lsa_header *lsah = lsa->data; + struct sr_link *srl; + + int length; + + osr_debug("SR (%s): Process Extended Link LSA 8.0.0.%u from %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); + + /* Sanity check */ + if (OspfSR.neighbors == NULL) { + flog_err(EC_OSPF_SR_INVALID_DB, + "SR (%s): Abort! no valid SR DataBase", __func__); + return; + } + + /* Get SR Node in hash table from Router ID */ + srn = (struct sr_node *)hash_get(OspfSR.neighbors, + (void *)&(lsah->adv_router), + (void *)sr_node_new); + + /* Initialize TLV browsing */ + length = lsa->size - OSPF_LSA_HEADER_SIZE; + for (tlvh = TLV_HDR_TOP(lsah); length > 0 && tlvh; + tlvh = TLV_HDR_NEXT(tlvh)) { + if (ntohs(tlvh->type) == EXT_TLV_LINK) { + /* Got Extended Link information */ + srl = get_ext_link_sid(tlvh, length); + /* Update SID if not null */ + if (srl != NULL) { + srl->instance = ntohl(lsah->id.s_addr); + update_ext_link_sid(srn, srl, lsa->flags); + } + } + length -= TLV_SIZE(tlvh); + } +} + +/* Delete Segment Routing from Extended Link LSA */ +void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa) +{ + struct listnode *node; + struct sr_link *srl; + struct sr_node *srn; + struct lsa_header *lsah = lsa->data; + uint32_t instance = ntohl(lsah->id.s_addr); + + osr_debug("SR (%s): Remove Extended Link LSA 8.0.0.%u from %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); + + /* Sanity check */ + if (OspfSR.neighbors == NULL) { + flog_err(EC_OSPF_SR_INVALID_DB, + "SR (%s): Abort! no valid SR DataBase", __func__); + return; + } + + /* Search SR Node in hash table from Router ID */ + srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, + (void *)&(lsah->adv_router)); + + /* + * SR-Node may be NULL if it has been remove previously when + * processing Router Information LSA deletion + */ + if (srn == NULL) { + flog_err(EC_OSPF_SR_INVALID_DB, + "SR (%s): Stop! no entry in SRDB for SR Node %pI4", + __func__, &lsah->adv_router); + return; + } + + /* Search for corresponding Segment Link */ + for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) + if (srl->instance == instance) + break; + + /* Remove Segment Link if found. Note that for Neighbors, only Global + * Adj/Lan-Adj SID are stored in the SR-DB */ + if ((srl != NULL) && (srl->instance == instance)) { + del_adj_sid(srl->nhlfe[0]); + del_adj_sid(srl->nhlfe[1]); + listnode_delete(srn->ext_link, srl); + XFREE(MTYPE_OSPF_SR_PARAMS, srl); + } +} + +/* Add (LAN)Adjacency-SID from Extended Link Information */ +void ospf_sr_ext_itf_add(struct ext_itf *exti) +{ + struct sr_node *srn = OspfSR.self; + struct sr_link *srl; + + osr_debug("SR (%s): Add Extended Link LSA 8.0.0.%u from self", __func__, + exti->instance); + + /* Sanity check */ + if (srn == NULL) + return; + + /* Initialize new Segment Routing Link */ + srl = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_link)); + srl->srn = srn; + srl->adv_router = srn->adv_router; + srl->itf_addr = exti->link.link_data; + srl->instance = + SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, exti->instance); + srl->remote_id = exti->link.link_id; + switch (exti->stype) { + case ADJ_SID: + srl->type = ADJ_SID; + /* Primary information */ + srl->flags[0] = exti->adj_sid[0].flags; + if (CHECK_FLAG(exti->adj_sid[0].flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + srl->sid[0] = GET_LABEL(ntohl(exti->adj_sid[0].value)); + else + srl->sid[0] = ntohl(exti->adj_sid[0].value); + if (exti->rmt_itf_addr.header.type == 0) + srl->nhlfe[0].nexthop = exti->link.link_id; + else + srl->nhlfe[0].nexthop = exti->rmt_itf_addr.value; + /* Backup Information if set */ + if (exti->adj_sid[1].header.type == 0) + break; + srl->flags[1] = exti->adj_sid[1].flags; + if (CHECK_FLAG(exti->adj_sid[1].flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + srl->sid[1] = GET_LABEL(ntohl(exti->adj_sid[1].value)); + else + srl->sid[1] = ntohl(exti->adj_sid[1].value); + if (exti->rmt_itf_addr.header.type == 0) + srl->nhlfe[1].nexthop = exti->link.link_id; + else + srl->nhlfe[1].nexthop = exti->rmt_itf_addr.value; + break; + case LAN_ADJ_SID: + srl->type = LAN_ADJ_SID; + /* Primary information */ + srl->flags[0] = exti->lan_sid[0].flags; + if (CHECK_FLAG(exti->lan_sid[0].flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + srl->sid[0] = GET_LABEL(ntohl(exti->lan_sid[0].value)); + else + srl->sid[0] = ntohl(exti->lan_sid[0].value); + if (exti->rmt_itf_addr.header.type == 0) + srl->nhlfe[0].nexthop = exti->lan_sid[0].neighbor_id; + else + srl->nhlfe[0].nexthop = exti->rmt_itf_addr.value; + /* Backup Information if set */ + if (exti->lan_sid[1].header.type == 0) + break; + srl->flags[1] = exti->lan_sid[1].flags; + if (CHECK_FLAG(exti->lan_sid[1].flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG)) + srl->sid[1] = GET_LABEL(ntohl(exti->lan_sid[1].value)); + else + srl->sid[1] = ntohl(exti->lan_sid[1].value); + if (exti->rmt_itf_addr.header.type == 0) + srl->nhlfe[1].nexthop = exti->lan_sid[1].neighbor_id; + else + srl->nhlfe[1].nexthop = exti->rmt_itf_addr.value; + break; + case PREF_SID: + case LOCAL_SID: + /* Wrong SID Type. Abort! */ + XFREE(MTYPE_OSPF_SR_PARAMS, srl); + return; + } + + /* Segment Routing Link is ready, update it */ + update_ext_link_sid(srn, srl, OSPF_LSA_SELF); +} + +/* Delete Prefix or (LAN)Adjacency-SID from Extended Link Information */ +void ospf_sr_ext_itf_delete(struct ext_itf *exti) +{ + struct listnode *node; + struct sr_node *srn = OspfSR.self; + struct sr_prefix *srp = NULL; + struct sr_link *srl = NULL; + uint32_t instance; + + osr_debug("SR (%s): Remove Extended LSA %u.0.0.%u from self", + __func__, exti->stype == PREF_SID ? 7 : 8, exti->instance); + + /* Sanity check: SR-Node and Extended Prefix/Link list may have been + * removed earlier when stopping OSPF or OSPF-SR */ + if (srn == NULL || srn->ext_prefix == NULL || srn->ext_link == NULL) + return; + + if (exti->stype == PREF_SID) { + instance = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_PREFIX_LSA, + exti->instance); + for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) + if (srp->instance == instance) + break; + + /* Uninstall Segment Prefix SID if found */ + if ((srp != NULL) && (srp->instance == instance)) + ospf_zebra_delete_prefix_sid(srp); + } else { + /* Search for corresponding Segment Link for self SR-Node */ + instance = SET_OPAQUE_LSID(OPAQUE_TYPE_EXTENDED_LINK_LSA, + exti->instance); + for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) + if (srl->instance == instance) + break; + + /* Remove Segment Link if found */ + if ((srl != NULL) && (srl->instance == instance)) { + del_adj_sid(srl->nhlfe[0]); + del_adj_sid(srl->nhlfe[1]); + listnode_delete(srn->ext_link, srl); + XFREE(MTYPE_OSPF_SR_PARAMS, srl); + } + } +} + +/* Update Segment Routing from Extended Prefix LSA */ +void ospf_sr_ext_prefix_lsa_update(struct ospf_lsa *lsa) +{ + struct sr_node *srn; + struct tlv_header *tlvh; + struct lsa_header *lsah = (struct lsa_header *)lsa->data; + struct sr_prefix *srp; + + int length; + + osr_debug("SR (%s): Process Extended Prefix LSA 7.0.0.%u from %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); + + /* Sanity check */ + if (OspfSR.neighbors == NULL) { + flog_err(EC_OSPF_SR_INVALID_DB, + "SR (%s): Abort! no valid SR DataBase", __func__); + return; + } + + /* Get SR Node in hash table from Router ID */ + srn = (struct sr_node *)hash_get(OspfSR.neighbors, + (void *)&(lsah->adv_router), + (void *)sr_node_new); + /* Initialize TLV browsing */ + length = lsa->size - OSPF_LSA_HEADER_SIZE; + for (tlvh = TLV_HDR_TOP(lsah); length > 0 && tlvh; + tlvh = TLV_HDR_NEXT(tlvh)) { + if (ntohs(tlvh->type) == EXT_TLV_LINK) { + /* Got Extended Link information */ + srp = get_ext_prefix_sid(tlvh, length); + /* Update SID if not null */ + if (srp != NULL) { + srp->instance = ntohl(lsah->id.s_addr); + update_ext_prefix_sid(srn, srp); + } + } + length -= TLV_SIZE(tlvh); + } +} + +/* Delete Segment Routing from Extended Prefix LSA */ +void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa) +{ + struct listnode *node; + struct sr_prefix *srp; + struct sr_node *srn; + struct lsa_header *lsah = (struct lsa_header *)lsa->data; + uint32_t instance = ntohl(lsah->id.s_addr); + + osr_debug("SR (%s): Remove Extended Prefix LSA 7.0.0.%u from %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); + + /* Sanity check */ + if (OspfSR.neighbors == NULL) { + flog_err(EC_OSPF_SR_INVALID_DB, + "SR (%s): Abort! no valid SR DataBase", __func__); + return; + } + + /* Search SR Node in hash table from Router ID */ + srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, + (void *)&(lsah->adv_router)); + + /* + * SR-Node may be NULL if it has been remove previously when + * processing Router Information LSA deletion + */ + if (srn == NULL) { + flog_err(EC_OSPF_SR_INVALID_DB, + "SR (%s): Stop! no entry in SRDB for SR Node %pI4", + __func__, &lsah->adv_router); + return; + } + + /* Search for corresponding Segment Prefix */ + for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) + if (srp->instance == instance) + break; + + /* Remove Prefix if found */ + if ((srp != NULL) && (srp->instance == instance)) { + ospf_zebra_delete_prefix_sid(srp); + listnode_delete(srn->ext_prefix, srp); + XFREE(MTYPE_OSPF_SR_PARAMS, srp); + } else { + flog_err( + EC_OSPF_SR_INVALID_DB, + "SR (%s): Didn't found corresponding SR Prefix 7.0.0.%u for SR Node %pI4", + __func__, GET_OPAQUE_ID(ntohl(lsah->id.s_addr)), + &lsah->adv_router); + } +} + +/* + * Update Prefix SID. Call by ospf_ext_pref_ism_change to + * complete initial CLI command at startup. + * + * @param ifp - Loopback interface + * @param pref - Prefix address of this interface + * + * @return - void + */ +void ospf_sr_update_local_prefix(struct interface *ifp, struct prefix *p) +{ + struct listnode *node; + struct sr_prefix *srp; + + /* Sanity Check */ + if ((ifp == NULL) || (p == NULL)) + return; + + /* + * Search if there is a Segment Prefix that correspond to this + * interface or prefix, and update it if found + */ + for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) { + if ((srp->nhlfe.ifindex == ifp->ifindex) + || ((IPV4_ADDR_SAME(&srp->prefv4.prefix, &p->u.prefix4)) + && (srp->prefv4.prefixlen == p->prefixlen))) { + + /* Update Interface & Prefix info */ + srp->nhlfe.ifindex = ifp->ifindex; + IPV4_ADDR_COPY(&srp->prefv4.prefix, &p->u.prefix4); + srp->prefv4.prefixlen = p->prefixlen; + srp->prefv4.family = p->family; + IPV4_ADDR_COPY(&srp->nhlfe.nexthop, &p->u.prefix4); + + /* OK. Let's Schedule Extended Prefix LSA */ + srp->instance = ospf_ext_schedule_prefix_index( + ifp, srp->sid, &srp->prefv4, srp->flags); + + osr_debug( + " |- Update Node SID %pFX - %u for self SR Node", + (struct prefix *)&srp->prefv4, srp->sid); + + /* Install SID if NO-PHP is set and not EXPLICIT-NULL */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG) + && !CHECK_FLAG(srp->flags, + EXT_SUBTLV_PREFIX_SID_EFLG)) { + srp->label_in = index2label(srp->sid, + OspfSR.self->srgb); + srp->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL; + ospf_zebra_update_prefix_sid(srp); + } + } + } +} + +/* + * Following functions are used to update MPLS LFIB after a SPF run + */ + +static void ospf_sr_nhlfe_update(struct hash_bucket *bucket, void *args) +{ + + struct sr_node *srn = (struct sr_node *)bucket->data; + struct listnode *node; + struct sr_prefix *srp; + bool old; + int rc; + + osr_debug(" |- Update Prefix for SR Node %pI4", &srn->adv_router); + + /* Skip Self SR Node */ + if (srn == OspfSR.self) + return; + + /* Update Extended Prefix */ + for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) { + + /* Keep track of valid route */ + old = srp->route != NULL; + + /* Compute the new NHLFE */ + rc = compute_prefix_nhlfe(srp); + + /* Check computation result */ + switch (rc) { + /* Routes are not know, remove old NHLFE if any to avoid loop */ + case -1: + if (old) + ospf_zebra_delete_prefix_sid(srp); + break; + /* Routes exist but are not ready, skip it */ + case 0: + break; + /* There is at least one route, update NHLFE */ + case 1: + ospf_zebra_update_prefix_sid(srp); + break; + default: + break; + } + } +} + +void ospf_sr_update_task(struct ospf *ospf) +{ + + struct timeval start_time, stop_time; + + /* Check ospf and SR status */ + if ((ospf == NULL) || (OspfSR.status != SR_UP)) + return; + + monotime(&start_time); + + osr_debug("SR (%s): Start SPF update", __func__); + + hash_iterate(OspfSR.neighbors, (void (*)(struct hash_bucket *, + void *))ospf_sr_nhlfe_update, + NULL); + + monotime(&stop_time); + + osr_debug("SR (%s): SPF Processing Time(usecs): %lld", __func__, + (stop_time.tv_sec - start_time.tv_sec) * 1000000LL + + (stop_time.tv_usec - start_time.tv_usec)); +} + +/* + * -------------------------------------- + * Following are vty command functions. + * -------------------------------------- + */ + +/* + * Segment Routing Router configuration + * + * Must be centralize as it concerns both Extended Link/Prefix LSA + * and Router Information LSA. Choose to call it from Extended Prefix + * write_config() call back. + * + * @param vty VTY output + * + * @return none + */ +void ospf_sr_config_write_router(struct vty *vty) +{ + struct listnode *node; + struct sr_prefix *srp; + uint32_t upper; + + if (OspfSR.status == SR_UP) + vty_out(vty, " segment-routing on\n"); + + upper = OspfSR.srgb.start + OspfSR.srgb.size - 1; + if ((OspfSR.srgb.start != DEFAULT_SRGB_LABEL) + || (OspfSR.srgb.size != DEFAULT_SRGB_SIZE)) + vty_out(vty, " segment-routing global-block %u %u", + OspfSR.srgb.start, upper); + + if ((OspfSR.srlb.start != DEFAULT_SRLB_LABEL) || + (OspfSR.srlb.end != DEFAULT_SRLB_END)) { + if ((OspfSR.srgb.start == DEFAULT_SRGB_LABEL) && + (OspfSR.srgb.size == DEFAULT_SRGB_SIZE)) + vty_out(vty, " segment-routing global-block %u %u", + OspfSR.srgb.start, upper); + vty_out(vty, " local-block %u %u\n", OspfSR.srlb.start, + OspfSR.srlb.end); + } else + vty_out(vty, "\n"); + + if (OspfSR.msd != 0) + vty_out(vty, " segment-routing node-msd %u\n", OspfSR.msd); + + if (OspfSR.self != NULL) { + for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) { + vty_out(vty, " segment-routing prefix %pFX index %u", + &srp->prefv4, srp->sid); + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_EFLG)) + vty_out(vty, " explicit-null\n"); + else if (CHECK_FLAG(srp->flags, + EXT_SUBTLV_PREFIX_SID_NPFLG)) + vty_out(vty, " no-php-flag\n"); + else + vty_out(vty, "\n"); + } + } +} + +DEFUN(ospf_sr_enable, + ospf_sr_enable_cmd, + "segment-routing on", + SR_STR + "Enable Segment Routing\n") +{ + + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + if (OspfSR.status != SR_OFF) + return CMD_SUCCESS; + + if (ospf->vrf_id != VRF_DEFAULT) { + vty_out(vty, + "Segment Routing is only supported in default VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + osr_debug("SR: Segment Routing: OFF -> ON"); + + /* Start Segment Routing */ + OspfSR.status = SR_ON; + ospf_sr_start(ospf); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_sr_enable, + no_ospf_sr_enable_cmd, + "no segment-routing [on]", + NO_STR + SR_STR + "Disable Segment Routing\n") +{ + + if (OspfSR.status == SR_OFF) + return CMD_SUCCESS; + + osr_debug("SR: Segment Routing: ON -> OFF"); + + /* Start by Disabling Extended Link & Prefix LSA */ + ospf_ext_update_sr(false); + + /* then, disable Router Information SR parameters */ + ospf_router_info_update_sr(false, OspfSR.self); + + /* Finally, stop Segment Routing */ + ospf_sr_stop(); + + return CMD_SUCCESS; +} + +static int ospf_sr_enabled(struct vty *vty) +{ + if (OspfSR.status != SR_OFF) + return 1; + + if (vty) + vty_out(vty, "%% OSPF SR is not turned on\n"); + + return 0; +} + +/* tell if two ranges [r1_lower, r1_upper] and [r2_lower,r2_upper] overlap */ +static bool ranges_overlap(uint32_t r1_lower, uint32_t r1_upper, + uint32_t r2_lower, uint32_t r2_upper) +{ + return !((r1_upper < r2_lower) || (r1_lower > r2_upper)); +} + + +/* tell if a range is valid */ +static bool sr_range_is_valid(uint32_t lower, uint32_t upper, uint32_t min_size) +{ + return (upper >= lower + min_size); +} + +/** + * Update SRGB and/or SRLB using new CLI values. + * + * @param gb_lower Lower bound of the SRGB + * @param gb_upper Upper bound of the SRGB + * @param lb_lower Lower bound of the SRLB + * @param lb_upper Upper bound of the SRLB + * + * @return 0 on success, -1 otherwise + */ +static int update_sr_blocks(uint32_t gb_lower, uint32_t gb_upper, + uint32_t lb_lower, uint32_t lb_upper) +{ + + /* Check if values have changed */ + bool gb_changed, lb_changed; + uint32_t gb_size = gb_upper - gb_lower + 1; + uint32_t lb_size = lb_upper - lb_lower + 1; + + gb_changed = + (OspfSR.srgb.size != gb_size || OspfSR.srgb.start != gb_lower); + lb_changed = + (OspfSR.srlb.end != lb_upper || OspfSR.srlb.start != lb_lower); + if (!gb_changed && !lb_changed) + return 0; + + /* Check if SR is correctly started i.e. Label Manager connected */ + if (OspfSR.status != SR_UP) { + OspfSR.srgb.size = gb_size; + OspfSR.srgb.start = gb_lower; + OspfSR.srlb.end = lb_upper; + OspfSR.srlb.start = lb_lower; + return 0; + } + + /* Release old SRGB if it has changed and is active. */ + if (gb_changed) { + + sr_global_block_delete(); + + /* Set new SRGB values - but do not reserve yet (we need to + * release the SRLB too) */ + OspfSR.srgb.size = gb_size; + OspfSR.srgb.start = gb_lower; + if (OspfSR.self != NULL) { + OspfSR.self->srgb.range_size = gb_size; + OspfSR.self->srgb.lower_bound = gb_lower; + } + } + /* Release old SRLB if it has changed and reserve new block as needed. + */ + if (lb_changed) { + + sr_local_block_delete(); + + /* Set new SRLB values */ + if (sr_local_block_init(lb_lower, lb_upper) < 0) { + ospf_sr_stop(); + return -1; + } + if (OspfSR.self != NULL) { + OspfSR.self->srlb.lower_bound = lb_lower; + OspfSR.self->srlb.range_size = lb_size; + } + } + + /* + * Try to reserve the new SRGB from the Label Manger. If the + * allocation fails, disable SR until new blocks are successfully + * allocated. + */ + if (gb_changed) { + if (sr_global_block_init(OspfSR.srgb.start, OspfSR.srgb.size) + < 0) { + ospf_sr_stop(); + return -1; + } + } + + /* Update Self SR-Node */ + if (OspfSR.self != NULL) { + /* SRGB is reserved, set Router Information parameters */ + ospf_router_info_update_sr(true, OspfSR.self); + + /* and update NHLFE entries */ + if (gb_changed) + hash_iterate(OspfSR.neighbors, + (void (*)(struct hash_bucket *, + void *))update_in_nhlfe, + NULL); + + /* and update (LAN)-Adjacency SID */ + if (lb_changed) + ospf_ext_link_srlb_update(); + } + + return 0; +} + +DEFUN(sr_global_label_range, sr_global_label_range_cmd, + "segment-routing global-block (16-1048575) (16-1048575) [local-block (16-1048575) (16-1048575)]", + SR_STR + "Segment Routing Global Block label range\n" + "Lower-bound range in decimal (16-1048575)\n" + "Upper-bound range in decimal (16-1048575)\n" + "Segment Routing Local Block label range\n" + "Lower-bound range in decimal (16-1048575)\n" + "Upper-bound range in decimal (16-1048575)\n") +{ + uint32_t lb_upper, lb_lower; + uint32_t gb_upper, gb_lower; + int idx_gb_low = 2, idx_gb_up = 3; + int idx_lb_low = 5, idx_lb_up = 6; + + /* Get lower and upper bound for mandatory global-block */ + gb_lower = strtoul(argv[idx_gb_low]->arg, NULL, 10); + gb_upper = strtoul(argv[idx_gb_up]->arg, NULL, 10); + + /* SRLB values are taken from vtysh if there, else use the known ones */ + lb_upper = argc > idx_lb_up ? strtoul(argv[idx_lb_up]->arg, NULL, 10) + : OspfSR.srlb.end; + lb_lower = argc > idx_lb_low ? strtoul(argv[idx_lb_low]->arg, NULL, 10) + : OspfSR.srlb.start; + + /* check correctness of input SRGB */ + if (!sr_range_is_valid(gb_lower, gb_upper, MIN_SRGB_SIZE)) { + vty_out(vty, "Invalid SRGB range\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* check correctness of SRLB */ + if (!sr_range_is_valid(lb_lower, lb_upper, MIN_SRLB_SIZE)) { + vty_out(vty, "Invalid SRLB range\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Validate SRGB against SRLB */ + if (ranges_overlap(gb_lower, gb_upper, lb_lower, lb_upper)) { + vty_out(vty, + "New SR Global Block (%u/%u) conflicts with Local Block (%u/%u)\n", + gb_lower, gb_upper, lb_lower, lb_upper); + return CMD_WARNING_CONFIG_FAILED; + } + + if (update_sr_blocks(gb_lower, gb_upper, lb_lower, lb_upper) < 0) + return CMD_WARNING_CONFIG_FAILED; + else + return CMD_SUCCESS; +} + +DEFUN(no_sr_global_label_range, no_sr_global_label_range_cmd, + "no segment-routing global-block [(16-1048575) (16-1048575) local-block (16-1048575) (16-1048575)]", + NO_STR SR_STR + "Segment Routing Global Block label range\n" + "Lower-bound range in decimal (16-1048575)\n" + "Upper-bound range in decimal (16-1048575)\n" + "Segment Routing Local Block label range\n" + "Lower-bound range in decimal (16-1048575)\n" + "Upper-bound range in decimal (16-1048575)\n") +{ + if (update_sr_blocks(DEFAULT_SRGB_LABEL, DEFAULT_SRGB_END, + DEFAULT_SRLB_LABEL, DEFAULT_SRLB_END) + < 0) + return CMD_WARNING_CONFIG_FAILED; + else + return CMD_SUCCESS; +} + +DEFUN (sr_node_msd, + sr_node_msd_cmd, + "segment-routing node-msd (1-16)", + SR_STR + "Maximum Stack Depth for this router\n" + "Maximum number of label that could be stack (1-16)\n") +{ + uint32_t msd; + int idx = 1; + + if (!ospf_sr_enabled(vty)) + return CMD_WARNING_CONFIG_FAILED; + + /* Get MSD */ + argv_find(argv, argc, "(1-16)", &idx); + msd = strtoul(argv[idx]->arg, NULL, 10); + if (msd < 1 || msd > MPLS_MAX_LABELS) { + vty_out(vty, "MSD must be comprise between 1 and %u\n", + MPLS_MAX_LABELS); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Check if value has changed */ + if (OspfSR.msd == msd) + return CMD_SUCCESS; + + /* Set this router MSD */ + OspfSR.msd = msd; + if (OspfSR.self != NULL) { + OspfSR.self->msd = msd; + + /* Set Router Information parameters if SR is UP */ + if (OspfSR.status == SR_UP) + ospf_router_info_update_sr(true, OspfSR.self); + } + + return CMD_SUCCESS; +} + +DEFUN (no_sr_node_msd, + no_sr_node_msd_cmd, + "no segment-routing node-msd [(1-16)]", + NO_STR + SR_STR + "Maximum Stack Depth for this router\n" + "Maximum number of label that could be stack (1-16)\n") +{ + + if (!ospf_sr_enabled(vty)) + return CMD_WARNING_CONFIG_FAILED; + + /* unset this router MSD */ + OspfSR.msd = 0; + if (OspfSR.self != NULL) { + OspfSR.self->msd = 0; + + /* Set Router Information parameters if SR is UP */ + if (OspfSR.status == SR_UP) + ospf_router_info_update_sr(true, OspfSR.self); + } + + return CMD_SUCCESS; +} + +DEFUN (sr_prefix_sid, + sr_prefix_sid_cmd, + "segment-routing prefix A.B.C.D/M index (0-65535) [no-php-flag|explicit-null]", + SR_STR + "Prefix SID\n" + "IPv4 Prefix as A.B.C.D/M\n" + "SID index for this prefix in decimal (0-65535)\n" + "Index value inside SRGB (lower_bound < index < upper_bound)\n" + "Don't request Penultimate Hop Popping (PHP)\n" + "Upstream neighbor must replace prefix-sid with explicit null label\n") +{ + int idx = 0; + struct prefix p, pexist; + uint32_t index; + struct listnode *node; + struct sr_prefix *srp, *exist = NULL; + struct interface *ifp; + bool no_php_flag = false; + bool exp_null = false; + bool index_in_use = false; + uint8_t desired_flags = 0; + + if (!ospf_sr_enabled(vty)) + return CMD_WARNING_CONFIG_FAILED; + + /* Get network prefix */ + argv_find(argv, argc, "A.B.C.D/M", &idx); + if (!str2prefix(argv[idx]->arg, &p)) { + vty_out(vty, "Invalid prefix format %s\n", argv[idx]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Get & verify index value */ + argv_find(argv, argc, "(0-65535)", &idx); + index = strtoul(argv[idx]->arg, NULL, 10); + if (index > OspfSR.srgb.size - 1) { + vty_out(vty, "Index %u must be lower than range size %u\n", + index, OspfSR.srgb.size); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Get options */ + no_php_flag = argv_find(argv, argc, "no-php-flag", &idx); + exp_null = argv_find(argv, argc, "explicit-null", &idx); + + desired_flags |= no_php_flag ? EXT_SUBTLV_PREFIX_SID_NPFLG : 0; + desired_flags |= exp_null ? EXT_SUBTLV_PREFIX_SID_NPFLG : 0; + desired_flags |= exp_null ? EXT_SUBTLV_PREFIX_SID_EFLG : 0; + + /* Search for an existing Prefix-SID */ + for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) { + if (prefix_same((struct prefix *)&srp->prefv4, &p)) + exist = srp; + if (srp->sid == index) { + index_in_use = true; + pexist = p; + } + } + + /* done if prefix segment already there with same index and flags */ + if (exist && exist->sid == index && exist->flags == desired_flags) + return CMD_SUCCESS; + + /* deny if index is already in use by a distinct prefix */ + if (!exist && index_in_use) { + vty_out(vty, "Index %u is already used by %pFX\n", index, + &pexist); + return CMD_WARNING_CONFIG_FAILED; + } + + /* First, remove old NHLFE if installed */ + if (exist && CHECK_FLAG(exist->flags, EXT_SUBTLV_PREFIX_SID_NPFLG) + && !CHECK_FLAG(exist->flags, EXT_SUBTLV_PREFIX_SID_EFLG)) + ospf_zebra_delete_prefix_sid(exist); + + /* Create new Extended Prefix to SRDB if not found */ + if (exist == NULL) { + srp = XCALLOC(MTYPE_OSPF_SR_PARAMS, sizeof(struct sr_prefix)); + IPV4_ADDR_COPY(&srp->prefv4.prefix, &p.u.prefix4); + srp->prefv4.prefixlen = p.prefixlen; + srp->prefv4.family = p.family; + srp->sid = index; + srp->type = LOCAL_SID; + } else { + /* we work on the existing SR prefix */ + srp = exist; + } + + /* Reset labels to handle flag update */ + srp->label_in = 0; + srp->nhlfe.label_out = 0; + srp->sid = index; + srp->flags = desired_flags; + + /* If NO PHP flag is present, compute NHLFE and set label */ + if (no_php_flag) { + srp->label_in = index2label(srp->sid, OspfSR.self->srgb); + srp->nhlfe.label_out = MPLS_LABEL_IMPLICIT_NULL; + } + + osr_debug("SR (%s): Add new index %u to Prefix %pFX", __func__, index, + (struct prefix *)&srp->prefv4); + + /* Get Interface and check if it is a Loopback */ + ifp = if_lookup_prefix(&p, VRF_DEFAULT); + if (ifp == NULL) { + /* + * Interface could be not yet available i.e. when this + * command is in the configuration file, OSPF is not yet + * ready. In this case, store the prefix SID for latter + * update of this Extended Prefix + */ + if (exist == NULL) + listnode_add(OspfSR.self->ext_prefix, srp); + zlog_info( + "Interface for prefix %pFX not found. Deferred LSA flooding", + &p); + return CMD_SUCCESS; + } + + if (!if_is_loopback(ifp)) { + vty_out(vty, "interface %s is not a Loopback\n", ifp->name); + XFREE(MTYPE_OSPF_SR_PARAMS, srp); + return CMD_WARNING_CONFIG_FAILED; + } + srp->nhlfe.ifindex = ifp->ifindex; + + /* Add SR Prefix if new */ + if (!exist) + listnode_add(OspfSR.self->ext_prefix, srp); + + /* Update Prefix SID if SR is UP */ + if (OspfSR.status == SR_UP) { + if (no_php_flag && !exp_null) + ospf_zebra_update_prefix_sid(srp); + } else + return CMD_SUCCESS; + + /* Finally, update Extended Prefix LSA id SR is UP */ + srp->instance = ospf_ext_schedule_prefix_index( + ifp, srp->sid, &srp->prefv4, srp->flags); + if (srp->instance == 0) { + vty_out(vty, "Unable to set index %u for prefix %pFX\n", + index, &p); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (no_sr_prefix_sid, + no_sr_prefix_sid_cmd, + "no segment-routing prefix A.B.C.D/M [index (0-65535)|no-php-flag|explicit-null]", + NO_STR + SR_STR + "Prefix SID\n" + "IPv4 Prefix as A.B.C.D/M\n" + "SID index for this prefix in decimal (0-65535)\n" + "Index value inside SRGB (lower_bound < index < upper_bound)\n" + "Don't request Penultimate Hop Popping (PHP)\n" + "Upstream neighbor must replace prefix-sid with explicit null label\n") +{ + int idx = 0; + struct prefix p; + struct listnode *node; + struct sr_prefix *srp; + struct interface *ifp; + bool found = false; + int rc; + + if (!ospf_sr_enabled(vty)) + return CMD_WARNING_CONFIG_FAILED; + + if (OspfSR.status != SR_UP) + return CMD_SUCCESS; + + /* Get network prefix */ + argv_find(argv, argc, "A.B.C.D/M", &idx); + rc = str2prefix(argv[idx]->arg, &p); + if (!rc) { + vty_out(vty, "Invalid prefix format %s\n", argv[idx]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + /* check that the prefix is already set */ + for (ALL_LIST_ELEMENTS_RO(OspfSR.self->ext_prefix, node, srp)) + if (IPV4_ADDR_SAME(&srp->prefv4.prefix, &p.u.prefix4) + && (srp->prefv4.prefixlen == p.prefixlen)) { + found = true; + break; + } + + if (!found) { + vty_out(vty, "Prefix %s is not found. Abort!\n", + argv[idx]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + osr_debug("SR (%s): Remove Prefix %pFX with index %u", __func__, + (struct prefix *)&srp->prefv4, srp->sid); + + /* Get Interface */ + ifp = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT); + if (ifp == NULL) { + vty_out(vty, "interface for prefix %s not found.\n", + argv[idx]->arg); + /* silently remove from list */ + listnode_delete(OspfSR.self->ext_prefix, srp); + XFREE(MTYPE_OSPF_SR_PARAMS, srp); + return CMD_SUCCESS; + } + + /* Update Extended Prefix LSA */ + if (!ospf_ext_schedule_prefix_index(ifp, 0, NULL, 0)) { + vty_out(vty, "No corresponding loopback interface. Abort!\n"); + return CMD_WARNING; + } + + /* Delete NHLFE if NO-PHP is set and EXPLICIT NULL not set */ + if (CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_NPFLG) + && !CHECK_FLAG(srp->flags, EXT_SUBTLV_PREFIX_SID_EFLG)) + ospf_zebra_delete_prefix_sid(srp); + + /* OK, all is clean, remove SRP from SRDB */ + listnode_delete(OspfSR.self->ext_prefix, srp); + XFREE(MTYPE_OSPF_SR_PARAMS, srp); + + return CMD_SUCCESS; +} + + +static char *sr_op2str(char *buf, size_t size, mpls_label_t label_in, + mpls_label_t label_out) +{ + if (size < 24) + return NULL; + + switch (label_out) { + case MPLS_LABEL_IMPLICIT_NULL: + snprintf(buf, size, "Pop(%u)", label_in); + break; + case MPLS_LABEL_IPV4_EXPLICIT_NULL: + if (label_in == MPLS_LABEL_IPV4_EXPLICIT_NULL) + snprintf(buf, size, "no-op."); + else + snprintf(buf, size, "Swap(%u, null)", label_in); + break; + case MPLS_INVALID_LABEL: + snprintf(buf, size, "no-op."); + break; + default: + snprintf(buf, size, "Swap(%u, %u)", label_in, label_out); + break; + } + return buf; +} + +static void show_sr_prefix(struct sbuf *sbuf, struct json_object *json, + struct sr_prefix *srp) +{ + + struct listnode *node; + struct ospf_path *path; + struct interface *itf; + json_object *json_route = NULL, *json_obj; + char pref[19]; + char sid[22]; + char op[32]; + char buf[PREFIX_STRLEN]; + int indent = 0; + + snprintfrr(pref, 19, "%pFX", (struct prefix *)&srp->prefv4); + snprintf(sid, 22, "SR Pfx (idx %u)", srp->sid); + if (json) { + json_object_string_add(json, "prefix", pref); + json_object_int_add(json, "sid", srp->sid); + json_object_int_add(json, "inputLabel", srp->label_in); + } else { + sbuf_push(sbuf, 0, "%18s %21s ", pref, sid); + } + + /* Check if it is a Local Node SID */ + if (srp->type == LOCAL_SID) { + itf = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT); + if (json) { + if (!json_route) { + json_route = json_object_new_array(); + json_object_object_add(json, "prefixRoute", + json_route); + } + json_obj = json_object_new_object(); + json_object_int_add(json_obj, "outputLabel", + srp->nhlfe.label_out); + json_object_string_add(json_obj, "interface", + itf ? itf->name : "-"); + json_object_string_addf(json_obj, "nexthop", "%pI4", + &srp->nhlfe.nexthop); + json_object_array_add(json_route, json_obj); + } else { + sbuf_push(sbuf, 0, "%20s %9s %15s\n", + sr_op2str(op, 32, srp->label_in, + srp->nhlfe.label_out), + itf ? itf->name : "-", + inet_ntop(AF_INET, &srp->nhlfe.nexthop, + buf, sizeof(buf))); + } + return; + } + + /* Check if we have a valid path for this prefix */ + if (srp->route == NULL) { + if (!json) { + sbuf_push(sbuf, 0, "\n"); + } + return; + } + + /* Process list of OSPF paths */ + for (ALL_LIST_ELEMENTS_RO(srp->route->paths, node, path)) { + itf = if_lookup_by_index(path->ifindex, VRF_DEFAULT); + if (json) { + if (!json_route) { + json_route = json_object_new_array(); + json_object_object_add(json, "prefixRoute", + json_route); + } + json_obj = json_object_new_object(); + json_object_int_add(json_obj, "outputLabel", + path->srni.label_out); + json_object_string_add(json_obj, "interface", + itf ? itf->name : "-"); + json_object_string_addf(json_obj, "nexthop", "%pI4", + &path->nexthop); + json_object_array_add(json_route, json_obj); + } else { + sbuf_push(sbuf, indent, "%20s %9s %15s\n", + sr_op2str(op, 32, srp->label_in, + path->srni.label_out), + itf ? itf->name : "-", + inet_ntop(AF_INET, &path->nexthop, buf, + sizeof(buf))); + /* Offset to align information for ECMP */ + indent = 43; + } + } +} + +static void show_sr_node(struct vty *vty, struct json_object *json, + struct sr_node *srn) +{ + + struct listnode *node; + struct sr_link *srl; + struct sr_prefix *srp; + struct interface *itf; + struct sbuf sbuf; + char pref[19]; + char sid[22]; + char op[32]; + char buf[PREFIX_STRLEN]; + uint32_t upper; + json_object *json_node = NULL, *json_algo, *json_obj; + json_object *json_prefix = NULL, *json_link = NULL; + + /* Sanity Check */ + if (srn == NULL) + return; + + sbuf_init(&sbuf, NULL, 0); + + if (json) { + json_node = json_object_new_object(); + json_object_string_addf(json_node, "routerID", "%pI4", + &srn->adv_router); + json_object_int_add(json_node, "srgbSize", + srn->srgb.range_size); + json_object_int_add(json_node, "srgbLabel", + srn->srgb.lower_bound); + json_object_int_add(json_node, "srlbSize", + srn->srlb.range_size); + json_object_int_add(json_node, "srlbLabel", + srn->srlb.lower_bound); + json_algo = json_object_new_array(); + json_object_object_add(json_node, "algorithms", json_algo); + for (int i = 0; i < ALGORITHM_COUNT; i++) { + if (srn->algo[i] == SR_ALGORITHM_UNSET) + continue; + json_obj = json_object_new_object(); + char tmp[2]; + + snprintf(tmp, sizeof(tmp), "%u", i); + json_object_string_add(json_obj, tmp, + srn->algo[i] == SR_ALGORITHM_SPF + ? "SPF" + : "S-SPF"); + json_object_array_add(json_algo, json_obj); + } + if (srn->msd != 0) + json_object_int_add(json_node, "nodeMsd", srn->msd); + } else { + sbuf_push(&sbuf, 0, "SR-Node: %pI4", &srn->adv_router); + upper = srn->srgb.lower_bound + srn->srgb.range_size - 1; + sbuf_push(&sbuf, 0, "\tSRGB: [%u/%u]", + srn->srgb.lower_bound, upper); + upper = srn->srlb.lower_bound + srn->srlb.range_size - 1; + sbuf_push(&sbuf, 0, "\tSRLB: [%u/%u]", + srn->srlb.lower_bound, upper); + sbuf_push(&sbuf, 0, "\tAlgo.(s): %s", + srn->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF"); + for (int i = 1; i < ALGORITHM_COUNT; i++) { + if (srn->algo[i] == SR_ALGORITHM_UNSET) + continue; + sbuf_push(&sbuf, 0, "/%s", + srn->algo[i] == SR_ALGORITHM_SPF ? "SPF" + : "S-SPF"); + } + if (srn->msd != 0) + sbuf_push(&sbuf, 0, "\tMSD: %u", srn->msd); + } + + if (!json) { + sbuf_push(&sbuf, 0, + "\n\n Prefix or Link Node or Adj. SID Label Operation Interface Nexthop\n"); + sbuf_push(&sbuf, 0, + "------------------ --------------------- -------------------- --------- ---------------\n"); + } + for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) { + if (json) { + if (!json_prefix) { + json_prefix = json_object_new_array(); + json_object_object_add(json_node, + "extendedPrefix", + json_prefix); + } + json_obj = json_object_new_object(); + show_sr_prefix(NULL, json_obj, srp); + json_object_array_add(json_prefix, json_obj); + } else { + show_sr_prefix(&sbuf, NULL, srp); + } + } + + for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) { + snprintfrr(pref, 19, "%pI4/32", &srl->itf_addr); + snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[0]); + itf = if_lookup_by_index(srl->nhlfe[0].ifindex, VRF_DEFAULT); + if (json) { + if (!json_link) { + json_link = json_object_new_array(); + json_object_object_add( + json_node, "extendedLink", json_link); + } + /* Primary Link */ + json_obj = json_object_new_object(); + json_object_string_add(json_obj, "prefix", pref); + json_object_int_add(json_obj, "sid", srl->sid[0]); + json_object_int_add(json_obj, "inputLabel", + srl->nhlfe[0].label_in); + json_object_int_add(json_obj, "outputLabel", + srl->nhlfe[0].label_out); + json_object_string_add(json_obj, "interface", + itf ? itf->name : "-"); + json_object_string_addf(json_obj, "nexthop", "%pI4", + &srl->nhlfe[0].nexthop); + json_object_array_add(json_link, json_obj); + /* Backup Link */ + json_obj = json_object_new_object(); + snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[1]); + json_object_string_add(json_obj, "prefix", pref); + json_object_int_add(json_obj, "sid", srl->sid[1]); + json_object_int_add(json_obj, "inputLabel", + srl->nhlfe[1].label_in); + json_object_int_add(json_obj, "outputLabel", + srl->nhlfe[1].label_out); + json_object_string_add(json_obj, "interface", + itf ? itf->name : "-"); + json_object_string_addf(json_obj, "nexthop", "%pI4", + &srl->nhlfe[1].nexthop); + json_object_array_add(json_link, json_obj); + } else { + sbuf_push(&sbuf, 0, "%18s %21s %20s %9s %15s\n", + pref, sid, + sr_op2str(op, 32, srl->nhlfe[0].label_in, + srl->nhlfe[0].label_out), + itf ? itf->name : "-", + inet_ntop(AF_INET, &srl->nhlfe[0].nexthop, + buf, sizeof(buf))); + snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[1]); + sbuf_push(&sbuf, 0, "%18s %21s %20s %9s %15s\n", + pref, sid, + sr_op2str(op, 32, srl->nhlfe[1].label_in, + srl->nhlfe[1].label_out), + itf ? itf->name : "-", + inet_ntop(AF_INET, &srl->nhlfe[1].nexthop, + buf, sizeof(buf))); + } + } + if (json) + json_object_array_add(json, json_node); + else + vty_out(vty, "%s\n", sbuf_buf(&sbuf)); + + sbuf_free(&sbuf); +} + +static void show_vty_srdb(struct hash_bucket *bucket, void *args) +{ + struct vty *vty = (struct vty *)args; + struct sr_node *srn = (struct sr_node *)bucket->data; + + show_sr_node(vty, NULL, srn); +} + +static void show_json_srdb(struct hash_bucket *bucket, void *args) +{ + struct json_object *json = (struct json_object *)args; + struct sr_node *srn = (struct sr_node *)bucket->data; + + show_sr_node(NULL, json, srn); +} + +DEFUN (show_ip_opsf_srdb, + show_ip_ospf_srdb_cmd, + "show ip ospf database segment-routing [adv-router A.B.C.D|self-originate] [json]", + SHOW_STR + IP_STR + OSPF_STR + "Database summary\n" + "Show Segment Routing Data Base\n" + "Advertising SR node\n" + "Advertising SR node ID (as an IP address)\n" + "Self-originated SR node\n" + JSON_STR) +{ + int idx = 0; + struct in_addr rid; + struct sr_node *srn; + bool uj = use_json(argc, argv); + json_object *json = NULL, *json_node_array = NULL; + + if (OspfSR.status == SR_OFF) { + vty_out(vty, "Segment Routing is disabled on this router\n"); + return CMD_WARNING; + } + + if (uj) { + json = json_object_new_object(); + json_node_array = json_object_new_array(); + json_object_string_addf(json, "srdbID", "%pI4", + &OspfSR.self->adv_router); + json_object_object_add(json, "srNodes", json_node_array); + } else { + vty_out(vty, + "\n\t\tOSPF Segment Routing database for ID %pI4\n\n", + &OspfSR.self->adv_router); + } + + if (argv_find(argv, argc, "self-originate", &idx)) { + srn = OspfSR.self; + show_sr_node(vty, json_node_array, srn); + if (uj) + vty_json(vty, json); + return CMD_SUCCESS; + } + + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &rid)) { + vty_out(vty, "Specified Router ID %s is invalid\n", + argv[idx]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + /* Get the SR Node from the SRDB */ + srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, + (void *)&rid); + show_sr_node(vty, json_node_array, srn); + if (uj) + vty_json(vty, json); + return CMD_SUCCESS; + } + + /* No parameters have been provided, Iterate through all the SRDB */ + if (uj) { + hash_iterate(OspfSR.neighbors, (void (*)(struct hash_bucket *, + void *))show_json_srdb, + (void *)json_node_array); + vty_json(vty, json); + } else { + hash_iterate(OspfSR.neighbors, (void (*)(struct hash_bucket *, + void *))show_vty_srdb, + (void *)vty); + } + return CMD_SUCCESS; +} + +/* Install new CLI commands */ +void ospf_sr_register_vty(void) +{ + install_element(VIEW_NODE, &show_ip_ospf_srdb_cmd); + + install_element(OSPF_NODE, &ospf_sr_enable_cmd); + install_element(OSPF_NODE, &no_ospf_sr_enable_cmd); + install_element(OSPF_NODE, &sr_global_label_range_cmd); + install_element(OSPF_NODE, &no_sr_global_label_range_cmd); + install_element(OSPF_NODE, &sr_node_msd_cmd); + install_element(OSPF_NODE, &no_sr_node_msd_cmd); + install_element(OSPF_NODE, &sr_prefix_sid_cmd); + install_element(OSPF_NODE, &no_sr_prefix_sid_cmd); +} diff --git a/ospfd/ospf_sr.h b/ospfd/ospf_sr.h new file mode 100644 index 0000000..fa61f66 --- /dev/null +++ b/ospfd/ospf_sr.h @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This is an implementation of Segment Routing + * as per RFC 8665 - OSPF Extensions for Segment Routing + * and RFC 8476 - Signaling Maximum SID Depth (MSD) Using OSPF + * + * Module name: Segment Routing header definitions + * + * Author: Olivier Dugeon + * Author: Anselme Sawadogo + * + * Copyright (C) 2016 - 2020 Orange Labs http://www.orange.com + */ + +#ifndef _FRR_OSPF_SR_H +#define _FRR_OSPF_SR_H + +/* macros and constants for segment routing */ +#define SET_RANGE_SIZE_MASK 0xffffff00 +#define GET_RANGE_SIZE_MASK 0x00ffffff +#define SET_LABEL_MASK 0xffffff00 +#define GET_LABEL_MASK 0x00ffffff +#define SET_RANGE_SIZE(range_size) ((range_size << 8) & SET_RANGE_SIZE_MASK) +#define GET_RANGE_SIZE(range_size) ((range_size >> 8) & GET_RANGE_SIZE_MASK) +#define SET_LABEL(label) ((label << 8) & SET_LABEL_MASK) +#define GET_LABEL(label) ((label >> 8) & GET_LABEL_MASK) + +/* smallest configurable SRGB / SRLB sizes */ +#define MIN_SRLB_SIZE 16 +#define MIN_SRGB_SIZE 16 + +/* Segment Routing TLVs as per RFC 8665 */ + +/* Segment ID could be a Label (3 bytes) or an Index (4 bytes) */ +#define SID_LABEL 3 +#define SID_LABEL_SIZE(U) (U - 1) +#define SID_INDEX 4 +#define SID_INDEX_SIZE(U) (U) + +/* Macro to log debug message */ +#define osr_debug(...) \ + do { \ + if (IS_DEBUG_OSPF_SR) \ + zlog_debug(__VA_ARGS__); \ + } while (0) + +/* Macro to check if SR Prefix has no valid route */ +#define IS_NO_ROUTE(srp) ((srp->route == NULL) || (srp->route->paths == NULL) \ + || list_isempty(srp->route->paths)) + +/* SID/Label Sub TLV - section 2.1 */ +#define SUBTLV_SID_LABEL 1 +#define SUBTLV_SID_LABEL_SIZE 4 +struct subtlv_sid_label { + /* Length is 3 (20 rightmost bits MPLS label) or 4 (32 bits SID) */ + struct tlv_header header; + uint32_t value; +}; + +/* + * Following section defines Segment Routing TLV (tag, length, value) + * structures, used in Router Information Opaque LSA. + */ + +/* RI SR-Algorithm TLV - section 3.1 */ +#define RI_SR_TLV_SR_ALGORITHM 8 +struct ri_sr_tlv_sr_algorithm { + struct tlv_header header; +#define SR_ALGORITHM_SPF 0 +#define SR_ALGORITHM_STRICT_SPF 1 +#define SR_ALGORITHM_UNSET 255 +#define ALGORITHM_COUNT 4 + /* Only 4 algorithms supported in this code */ + uint8_t value[ALGORITHM_COUNT]; +}; + +/* RI SID/Label Range TLV used for SRGB & SRLB - section 3.2 & 3.3 */ +#define RI_SR_TLV_SRGB_LABEL_RANGE 9 +#define RI_SR_TLV_SRLB_LABEL_RANGE 14 +#define RI_SR_TLV_LABEL_RANGE_SIZE 12 +struct ri_sr_tlv_sid_label_range { + struct tlv_header header; +/* Only 24 upper most bits are significant */ +#define SID_RANGE_LABEL_LENGTH 3 + uint32_t size; + /* A SID/Label sub-TLV will follow. */ + struct subtlv_sid_label lower; +}; + +/* RI Node/MSD TLV as per RFC 8476 */ +#define RI_SR_TLV_NODE_MSD 12 +#define RI_SR_TLV_NODE_MSD_SIZE 4 +struct ri_sr_tlv_node_msd { + struct tlv_header header; + uint8_t subtype; /* always = 1 */ + uint8_t value; + uint16_t padding; +}; + +/* + * Following section defines Segment Routing TLV (tag, length, value) + * structures, used in Extended Prefix/Link Opaque LSA. + */ + +/* Adj-SID and LAN-Ajd-SID subtlvs' flags */ +#define EXT_SUBTLV_LINK_ADJ_SID_BFLG 0x80 +#define EXT_SUBTLV_LINK_ADJ_SID_VFLG 0x40 +#define EXT_SUBTLV_LINK_ADJ_SID_LFLG 0x20 +#define EXT_SUBTLV_LINK_ADJ_SID_SFLG 0x10 + +/* Prefix SID subtlv Flags */ +#define EXT_SUBTLV_PREFIX_SID_NPFLG 0x40 +#define EXT_SUBTLV_PREFIX_SID_MFLG 0x20 +#define EXT_SUBTLV_PREFIX_SID_EFLG 0x10 +#define EXT_SUBTLV_PREFIX_SID_VFLG 0x08 +#define EXT_SUBTLV_PREFIX_SID_LFLG 0x04 + +/* SID/Label Binding subtlv Flags */ +#define EXT_SUBTLV_SID_BINDING_MFLG 0x80 + +/* Extended Prefix Range TLV - section 4 */ +#define EXT_TLV_PREF_RANGE 2 +#define EXT_SUBTLV_PREFIX_RANGE_SIZE 12 +struct ext_tlv_prefix_range { + struct tlv_header header; + uint8_t pref_length; + uint8_t af; + uint16_t range_size; + uint8_t flags; + uint8_t reserved[3]; + struct in_addr address; +}; + +/* Prefix SID Sub-TLV - section 5 */ +#define EXT_SUBTLV_PREFIX_SID 2 +#define EXT_SUBTLV_PREFIX_SID_SIZE 8 +struct ext_subtlv_prefix_sid { + struct tlv_header header; + uint8_t flags; + uint8_t reserved; + uint8_t mtid; + uint8_t algorithm; + uint32_t value; +}; + +/* Adj-SID Sub-TLV - section 6.1 */ +#define EXT_SUBTLV_ADJ_SID 2 +#define EXT_SUBTLV_ADJ_SID_SIZE 8 +struct ext_subtlv_adj_sid { + struct tlv_header header; + uint8_t flags; + uint8_t reserved; + uint8_t mtid; + uint8_t weight; + uint32_t value; +}; + +/* LAN Adj-SID Sub-TLV - section 6.2 */ +#define EXT_SUBTLV_LAN_ADJ_SID 3 +#define EXT_SUBTLV_LAN_ADJ_SID_SIZE 12 +struct ext_subtlv_lan_adj_sid { + struct tlv_header header; + uint8_t flags; + uint8_t reserved; + uint8_t mtid; + uint8_t weight; + struct in_addr neighbor_id; + uint32_t value; +}; + +/* + * Following section define structure used to manage Segment Routing + * information and TLVs / SubTLVs + */ +/* Default min and size of SR Global Block label range */ +#define DEFAULT_SRGB_LABEL 16000 +#define DEFAULT_SRGB_SIZE 8000 +#define DEFAULT_SRGB_END (DEFAULT_SRGB_LABEL + DEFAULT_SRGB_SIZE - 1) + +/* Default min and size of SR Local Block label range */ +#define DEFAULT_SRLB_LABEL 15000 +#define DEFAULT_SRLB_SIZE 1000 +#define DEFAULT_SRLB_END (DEFAULT_SRLB_LABEL + DEFAULT_SRLB_SIZE - 1) + +/* Structure aggregating SR Range Block info retrieved from an lsa */ +struct sr_block { + uint32_t range_size; + uint32_t lower_bound; +}; + +/* Segment Routing Global Block allocation */ +struct sr_global_block { + bool reserved; + uint32_t start; + uint32_t size; +}; + +/* Segment Routing Local Block allocation */ +struct sr_local_block { + bool reserved; + uint32_t start; + uint32_t end; + uint32_t current; + uint32_t max_block; + uint64_t *used_mark; +}; +#define SRLB_BLOCK_SIZE 64 + +/* SID type to make difference between loopback interfaces and others */ +enum sid_type { PREF_SID, LOCAL_SID, ADJ_SID, LAN_ADJ_SID }; + +/* Status of Segment Routing: Off (Disable), On (Enable), (Up) Started */ +enum sr_status { SR_OFF, SR_ON, SR_UP }; + +/* Structure aggregating all OSPF Segment Routing information for the node */ +struct ospf_sr_db { + /* Status of Segment Routing */ + enum sr_status status; + + /* Flooding Scope: Area = 10 or AS = 11 */ + uint8_t scope; + + /* FRR SR node */ + struct sr_node *self; + + /* List of neighbour SR nodes */ + struct hash *neighbors; + + /* Local SR info announced in Router Info LSA */ + + /* Algorithms supported by the node */ + uint8_t algo[ALGORITHM_COUNT]; + /* + * Segment Routing Global Block i.e. label range + * Only one range supported in this code + */ + struct sr_global_block srgb; + + /* Segment Routing Local Block */ + struct sr_local_block srlb; + + /* Maximum SID Depth supported by the node */ + uint8_t msd; + + /* Thread timer to start Label Manager */ + struct event *t_start_lm; +}; + +/* Structure aggregating all received SR info from LSAs by node */ +struct sr_node { + struct in_addr adv_router; /* used to identify sender of LSA */ + /* 24-bit Opaque-ID field value according to RFC 7684 specification */ + uint32_t instance; + + uint8_t algo[ALGORITHM_COUNT]; /* Algorithms supported by the node */ + struct sr_block srgb; /* Segment Routing Global Block */ + struct sr_block srlb; /* Segment Routing Local Block */ + uint8_t msd; /* Maximum SID Depth */ + + /* List of Prefix & Link advertise by this node */ + struct list *ext_prefix; /* For Node SID */ + struct list *ext_link; /* For Adjacency SID */ + + /* Pointer to FRR SR-Node or NULL if it is not a neighbor */ + struct sr_node *neighbor; +}; + +/* Segment Routing - NHLFE info: support IPv4 Only */ +struct sr_nhlfe { + struct in_addr nexthop; + ifindex_t ifindex; + mpls_label_t label_in; + mpls_label_t label_out; +}; + +/* Structure aggregating all Segment Routing Link information */ +/* Link are generally advertised by pair: primary + backup */ +struct sr_link { + struct in_addr adv_router; /* used to identify sender of LSA */ + /* 24-bit Opaque-ID field value according to RFC 7684 specification */ + uint32_t instance; + + /* Addressed (remote) router id */ + struct in_addr remote_id; + + /* Interface address */ + struct in_addr itf_addr; + + /* Flags to manage this link parameters. */ + uint8_t flags[2]; + + /* Segment Routing ID */ + uint32_t sid[2]; + enum sid_type type; + + /* SR NHLFE (Primary + Backup) for this link */ + struct sr_nhlfe nhlfe[2]; + + /* Back pointer to SR Node which advertise this Link */ + struct sr_node *srn; +}; + +/* Structure aggregating all Segment Routing Prefix information */ +struct sr_prefix { + struct in_addr adv_router; /* used to identify sender of LSA */ + /* 24-bit Opaque-ID field value according to RFC 7684 specification */ + uint32_t instance; + + /* Prefix itself */ + struct prefix_ipv4 prefv4; + + /* Flags to manage this prefix parameters. */ + uint8_t flags; + + /* Segment Routing ID */ + uint32_t sid; + enum sid_type type; + + /* Incoming label for this prefix */ + mpls_label_t label_in; + + /* Back pointer to OSPF Route for remote prefix */ + struct ospf_route *route; + + /* NHLFE for local prefix */ + struct sr_nhlfe nhlfe; + + /* Back pointer to SR Node which advertise this Prefix */ + struct sr_node *srn; +}; + +/* Prototypes definition */ +/* Segment Routing initialisation functions */ +extern int ospf_sr_init(void); +extern void ospf_sr_term(void); +extern void ospf_sr_finish(void); +/* Segment Routing label allocation functions */ +extern mpls_label_t ospf_sr_local_block_request_label(void); +extern int ospf_sr_local_block_release_label(mpls_label_t label); +/* Segment Routing LSA update & delete functions */ +extern void ospf_sr_ri_lsa_update(struct ospf_lsa *lsa); +extern void ospf_sr_ri_lsa_delete(struct ospf_lsa *lsa); +extern void ospf_sr_ext_link_lsa_update(struct ospf_lsa *lsa); +extern void ospf_sr_ext_link_lsa_delete(struct ospf_lsa *lsa); +extern void ospf_sr_ext_prefix_lsa_update(struct ospf_lsa *lsa); +extern void ospf_sr_ext_prefix_lsa_delete(struct ospf_lsa *lsa); +/* Segment Routing Extending Link management */ +struct ext_itf; +extern void ospf_sr_ext_itf_add(struct ext_itf *exti); +extern void ospf_sr_ext_itf_delete(struct ext_itf *exti); +/* Segment Routing configuration functions */ +extern void ospf_sr_config_write_router(struct vty *vty); +extern void ospf_sr_update_local_prefix(struct interface *ifp, + struct prefix *p); +/* Segment Routing re-routing function */ +extern void ospf_sr_update_task(struct ospf *ospf); + +/* Support for TI-LFA */ +extern mpls_label_t ospf_sr_get_prefix_sid_by_id(struct in_addr *id); +extern mpls_label_t ospf_sr_get_adj_sid_by_id(struct in_addr *root_id, + struct in_addr *neighbor_id); +extern struct sr_node *ospf_sr_node_create(struct in_addr *rid); + +#endif /* _FRR_OSPF_SR_H */ diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c new file mode 100644 index 0000000..3cf39e5 --- /dev/null +++ b/ospfd/ospf_te.c @@ -0,0 +1,4423 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This is an implementation of RFC3630 + * Copyright (C) 2001 KDD R&D Laboratories, Inc. + * http://www.kddlabs.co.jp/ + * + * Copyright (C) 2012 Orange Labs + * http://www.orange.com + */ + +/* Add support of RFC7471 */ +/* Add support of RFC5392, RFC6827 */ + +#include +#include + +#include "linklist.h" +#include "prefix.h" +#include "vrf.h" +#include "if.h" +#include "table.h" +#include "memory.h" +#include "command.h" +#include "vty.h" +#include "stream.h" +#include "log.h" +#include "frrevent.h" +#include "hash.h" +#include "sockunion.h" /* for inet_aton() */ +#include "network.h" +#include "link_state.h" +#include "zclient.h" +#include "printfrr.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_te.h" +#include "ospfd/ospf_sr.h" +#include "ospfd/ospf_ri.h" +#include "ospfd/ospf_ext.h" +#include "ospfd/ospf_vty.h" +#include "ospfd/ospf_errors.h" + +/* + * Global variable to manage Opaque-LSA/MPLS-TE on this node. + * Note that all parameter values are stored in network byte order. + */ +struct ospf_mpls_te OspfMplsTE; + +static const char *const mode2text[] = {"Off", "AS", "Area"}; + + +/*------------------------------------------------------------------------* + * Following are initialize/terminate functions for MPLS-TE handling. + *------------------------------------------------------------------------*/ + +static int ospf_mpls_te_new_if(struct interface *ifp); +static int ospf_mpls_te_del_if(struct interface *ifp); +static void ospf_mpls_te_ism_change(struct ospf_interface *oi, int old_status); +static void ospf_mpls_te_nsm_change(struct ospf_neighbor *nbr, int old_status); +static void ospf_mpls_te_config_write_router(struct vty *vty); +static void ospf_mpls_te_show_info(struct vty *vty, struct json_object *json, + struct ospf_lsa *lsa); +static int ospf_mpls_te_lsa_originate_area(void *arg); +static int ospf_mpls_te_lsa_inter_as_as(void *arg); +static int ospf_mpls_te_lsa_inter_as_area(void *arg); +static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa); +static int ospf_mpls_te_lsa_update(struct ospf_lsa *lsa); +static int ospf_mpls_te_lsa_delete(struct ospf_lsa *lsa); + +static void del_mpls_te_link(void *val); +static void ospf_mpls_te_register_vty(void); + +int ospf_mpls_te_init(void) +{ + int rc; + + /* Register Opaque AREA LSA Type 1 for Traffic Engineering */ + rc = ospf_register_opaque_functab( + OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA, + ospf_mpls_te_new_if, + ospf_mpls_te_del_if, + ospf_mpls_te_ism_change, + ospf_mpls_te_nsm_change, + ospf_mpls_te_config_write_router, + NULL, /* ospf_mpls_te_config_write_if */ + NULL, /* ospf_mpls_te_config_write_debug */ + ospf_mpls_te_show_info, ospf_mpls_te_lsa_originate_area, + ospf_mpls_te_lsa_refresh, + ospf_mpls_te_lsa_update, /* ospf_mpls_te_new_lsa_hook */ + ospf_mpls_te_lsa_delete /* ospf_mpls_te_del_lsa_hook */); + if (rc != 0) { + flog_warn( + EC_OSPF_OPAQUE_REGISTRATION, + "MPLS-TE (%s): Failed to register Traffic Engineering functions", + __func__); + return rc; + } + + /* + * Wee need also to register Opaque LSA Type 6 i.e. Inter-AS RFC5392 for + * both AREA and AS at least to have the possibility to call the show() + * function when looking to the opaque LSA of the OSPF database. + */ + rc = ospf_register_opaque_functab(OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_INTER_AS_LSA, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + ospf_mpls_te_show_info, + ospf_mpls_te_lsa_inter_as_area, + ospf_mpls_te_lsa_refresh, NULL, NULL); + if (rc != 0) { + flog_warn( + EC_OSPF_OPAQUE_REGISTRATION, + "MPLS-TE (%s): Failed to register Inter-AS with Area scope", + __func__); + return rc; + } + + rc = ospf_register_opaque_functab(OSPF_OPAQUE_AS_LSA, + OPAQUE_TYPE_INTER_AS_LSA, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + ospf_mpls_te_show_info, + ospf_mpls_te_lsa_inter_as_as, + ospf_mpls_te_lsa_refresh, NULL, NULL); + if (rc != 0) { + flog_warn( + EC_OSPF_OPAQUE_REGISTRATION, + "MPLS-TE (%s): Failed to register Inter-AS with AS scope", + __func__); + return rc; + } + + memset(&OspfMplsTE, 0, sizeof(OspfMplsTE)); + OspfMplsTE.enabled = false; + OspfMplsTE.export = false; + OspfMplsTE.inter_as = Off; + OspfMplsTE.iflist = list_new(); + OspfMplsTE.iflist->del = del_mpls_te_link; + + ospf_mpls_te_register_vty(); + + return rc; +} + +void ospf_mpls_te_term(void) +{ + list_delete(&OspfMplsTE.iflist); + + ospf_delete_opaque_functab(OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA); + ospf_delete_opaque_functab(OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_INTER_AS_LSA); + ospf_delete_opaque_functab(OSPF_OPAQUE_AS_LSA, + OPAQUE_TYPE_INTER_AS_LSA); + + OspfMplsTE.enabled = false; + OspfMplsTE.inter_as = Off; + OspfMplsTE.export = false; + + return; +} + +void ospf_mpls_te_finish(void) +{ + OspfMplsTE.enabled = false; + OspfMplsTE.inter_as = Off; + OspfMplsTE.export = false; +} + +/*------------------------------------------------------------------------* + * Following are control functions for MPLS-TE parameters management. + *------------------------------------------------------------------------*/ +static void del_mpls_te_link(void *val) +{ + XFREE(MTYPE_OSPF_MPLS_TE, val); + return; +} + +static uint32_t get_mpls_te_instance_value(void) +{ + static uint32_t seqno = 0; + + if (seqno < MAX_LEGAL_TE_INSTANCE_NUM) + seqno += 1; + else + seqno = 1; /* Avoid zero. */ + + return seqno; +} + +static struct mpls_te_link *lookup_linkparams_by_ifp(struct interface *ifp) +{ + struct listnode *node, *nnode; + struct mpls_te_link *lp; + + for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) + if (lp->ifp == ifp) + return lp; + + return NULL; +} + +static struct mpls_te_link *lookup_linkparams_by_instance(struct ospf_lsa *lsa) +{ + struct listnode *node; + struct mpls_te_link *lp; + unsigned int key = GET_OPAQUE_ID(ntohl(lsa->data->id.s_addr)); + + for (ALL_LIST_ELEMENTS_RO(OspfMplsTE.iflist, node, lp)) + if (lp->instance == key) + return lp; + + ote_debug("MPLS-TE (%s): Entry not found: key(%x)", __func__, key); + return NULL; +} + +static void ospf_mpls_te_foreach_area( + void (*func)(struct mpls_te_link *lp, enum lsa_opcode sched_opcode), + enum lsa_opcode sched_opcode) +{ + struct listnode *node, *nnode; + struct listnode *node2; + struct mpls_te_link *lp; + struct ospf_area *area; + + for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) { + /* Skip Inter-AS TEv2 Links */ + if (IS_INTER_AS(lp->type)) + continue; + if ((area = lp->area) == NULL) + continue; + if (CHECK_FLAG(lp->flags, LPFLG_LOOKUP_DONE)) + continue; + + if (func != NULL) + (*func)(lp, sched_opcode); + + for (node2 = listnextnode(node); node2; + node2 = listnextnode(node2)) + if ((lp = listgetdata(node2)) != NULL) + if (lp->area != NULL) + if (IPV4_ADDR_SAME(&lp->area->area_id, + &area->area_id)) + SET_FLAG(lp->flags, + LPFLG_LOOKUP_DONE); + } + + for (ALL_LIST_ELEMENTS_RO(OspfMplsTE.iflist, node, lp)) + if (lp->area != NULL) + UNSET_FLAG(lp->flags, LPFLG_LOOKUP_DONE); + + return; +} + +static void set_mpls_te_router_addr(struct in_addr ipv4) +{ + OspfMplsTE.router_addr.header.type = htons(TE_TLV_ROUTER_ADDR); + OspfMplsTE.router_addr.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); + OspfMplsTE.router_addr.value = ipv4; + return; +} + +static void set_linkparams_link_header(struct mpls_te_link *lp) +{ + uint16_t length = 0; + + /* TE_LINK_SUBTLV_LINK_TYPE */ + if (ntohs(lp->link_type.header.type) != 0) + length += TLV_SIZE(&lp->link_type.header); + + /* TE_LINK_SUBTLV_LINK_ID */ + if (ntohs(lp->link_id.header.type) != 0) + length += TLV_SIZE(&lp->link_id.header); + + /* TE_LINK_SUBTLV_LCLIF_IPADDR */ + if (lp->lclif_ipaddr.header.type != 0) + length += TLV_SIZE(&lp->lclif_ipaddr.header); + + /* TE_LINK_SUBTLV_RMTIF_IPADDR */ + if (lp->rmtif_ipaddr.header.type != 0) + length += TLV_SIZE(&lp->rmtif_ipaddr.header); + + /* TE_LINK_SUBTLV_TE_METRIC */ + if (ntohs(lp->te_metric.header.type) != 0) + length += TLV_SIZE(&lp->te_metric.header); + + /* TE_LINK_SUBTLV_MAX_BW */ + if (ntohs(lp->max_bw.header.type) != 0) + length += TLV_SIZE(&lp->max_bw.header); + + /* TE_LINK_SUBTLV_MAX_RSV_BW */ + if (ntohs(lp->max_rsv_bw.header.type) != 0) + length += TLV_SIZE(&lp->max_rsv_bw.header); + + /* TE_LINK_SUBTLV_UNRSV_BW */ + if (ntohs(lp->unrsv_bw.header.type) != 0) + length += TLV_SIZE(&lp->unrsv_bw.header); + + /* TE_LINK_SUBTLV_RSC_CLSCLR */ + if (ntohs(lp->rsc_clsclr.header.type) != 0) + length += TLV_SIZE(&lp->rsc_clsclr.header); + + /* TE_LINK_SUBTLV_LLRI */ + if (ntohs(lp->llri.header.type) != 0) + length += TLV_SIZE(&lp->llri.header); + + /* TE_LINK_SUBTLV_RIP */ + if (ntohs(lp->rip.header.type) != 0) + length += TLV_SIZE(&lp->rip.header); + + /* TE_LINK_SUBTLV_RAS */ + if (ntohs(lp->ras.header.type) != 0) + length += TLV_SIZE(&lp->ras.header); + + /* TE_LINK_SUBTLV_LRRID */ + if (ntohs(lp->lrrid.header.type) != 0) + length += TLV_SIZE(&lp->lrrid.header); + + /* TE_LINK_SUBTLV_AV_DELAY */ + if (ntohs(lp->av_delay.header.type) != 0) + length += TLV_SIZE(&lp->av_delay.header); + + /* TE_LINK_SUBTLV_MM_DELAY */ + if (ntohs(lp->mm_delay.header.type) != 0) + length += TLV_SIZE(&lp->mm_delay.header); + + /* TE_LINK_SUBTLV_DELAY_VAR */ + if (ntohs(lp->delay_var.header.type) != 0) + length += TLV_SIZE(&lp->delay_var.header); + + /* TE_LINK_SUBTLV_PKT_LOSS */ + if (ntohs(lp->pkt_loss.header.type) != 0) + length += TLV_SIZE(&lp->pkt_loss.header); + + /* TE_LINK_SUBTLV_RES_BW */ + if (ntohs(lp->res_bw.header.type) != 0) + length += TLV_SIZE(&lp->res_bw.header); + + /* TE_LINK_SUBTLV_AVA_BW */ + if (ntohs(lp->ava_bw.header.type) != 0) + length += TLV_SIZE(&lp->ava_bw.header); + + /* TE_LINK_SUBTLV_USE_BW */ + if (ntohs(lp->use_bw.header.type) != 0) + length += TLV_SIZE(&lp->use_bw.header); + + lp->link_header.header.type = htons(TE_TLV_LINK); + lp->link_header.header.length = htons(length); + + return; +} + +static void set_linkparams_link_type(struct ospf_interface *oi, + struct mpls_te_link *lp) +{ + lp->link_type.header.type = htons(TE_LINK_SUBTLV_LINK_TYPE); + lp->link_type.header.length = htons(TE_LINK_SUBTLV_TYPE_SIZE); + + switch (oi->type) { + case OSPF_IFTYPE_POINTOPOINT: + lp->link_type.link_type.value = LINK_TYPE_SUBTLV_VALUE_PTP; + break; + case OSPF_IFTYPE_BROADCAST: + case OSPF_IFTYPE_NBMA: + lp->link_type.link_type.value = LINK_TYPE_SUBTLV_VALUE_MA; + break; + default: + /* Not supported yet. */ /* XXX */ + lp->link_type.header.type = htons(0); + break; + } + return; +} + +static void set_linkparams_link_id(struct mpls_te_link *lp, + struct in_addr link_id) +{ + + lp->link_id.header.type = htons(TE_LINK_SUBTLV_LINK_ID); + lp->link_id.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); + lp->link_id.value = link_id; + return; +} + +static void set_linkparams_lclif_ipaddr(struct mpls_te_link *lp, + struct in_addr lclif) +{ + + lp->lclif_ipaddr.header.type = htons(TE_LINK_SUBTLV_LCLIF_IPADDR); + lp->lclif_ipaddr.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); + lp->lclif_ipaddr.value[0] = lclif; + return; +} + +static void set_linkparams_rmtif_ipaddr(struct mpls_te_link *lp, + struct in_addr rmtif) +{ + + lp->rmtif_ipaddr.header.type = htons(TE_LINK_SUBTLV_RMTIF_IPADDR); + lp->rmtif_ipaddr.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); + lp->rmtif_ipaddr.value[0] = rmtif; + return; +} + +static void set_linkparams_te_metric(struct mpls_te_link *lp, + uint32_t te_metric) +{ + lp->te_metric.header.type = htons(TE_LINK_SUBTLV_TE_METRIC); + lp->te_metric.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); + lp->te_metric.value = htonl(te_metric); + return; +} + +static void set_linkparams_max_bw(struct mpls_te_link *lp, float fp) +{ + lp->max_bw.header.type = htons(TE_LINK_SUBTLV_MAX_BW); + lp->max_bw.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); + lp->max_bw.value = htonf(fp); + return; +} + +static void set_linkparams_max_rsv_bw(struct mpls_te_link *lp, float fp) +{ + lp->max_rsv_bw.header.type = htons(TE_LINK_SUBTLV_MAX_RSV_BW); + lp->max_rsv_bw.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); + lp->max_rsv_bw.value = htonf(fp); + return; +} + +static void set_linkparams_unrsv_bw(struct mpls_te_link *lp, int priority, + float fp) +{ + /* Note that TLV-length field is the size of array. */ + lp->unrsv_bw.header.type = htons(TE_LINK_SUBTLV_UNRSV_BW); + lp->unrsv_bw.header.length = htons(TE_LINK_SUBTLV_UNRSV_SIZE); + lp->unrsv_bw.value[priority] = htonf(fp); + return; +} + +static void set_linkparams_rsc_clsclr(struct mpls_te_link *lp, + uint32_t classcolor) +{ + lp->rsc_clsclr.header.type = htons(TE_LINK_SUBTLV_RSC_CLSCLR); + lp->rsc_clsclr.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); + lp->rsc_clsclr.value = htonl(classcolor); + return; +} + +static void set_linkparams_inter_as(struct mpls_te_link *lp, + struct in_addr addr, uint32_t as) +{ + + /* Set the Remote ASBR IP address and then the associated AS number */ + lp->rip.header.type = htons(TE_LINK_SUBTLV_RIP); + lp->rip.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); + lp->rip.value = addr; + + lp->ras.header.type = htons(TE_LINK_SUBTLV_RAS); + lp->ras.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); + lp->ras.value = htonl(as); + + /* Set Type & Flooding flag accordingly */ + lp->type = INTER_AS; + if (OspfMplsTE.inter_as == AS) + SET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS); + else + UNSET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS); +} + +static void unset_linkparams_inter_as(struct mpls_te_link *lp) +{ + + /* Reset the Remote ASBR IP address and then the associated AS number */ + lp->rip.header.type = htons(0); + lp->rip.header.length = htons(0); + lp->rip.value.s_addr = htonl(0); + + lp->ras.header.type = htons(0); + lp->ras.header.length = htons(0); + lp->ras.value = htonl(0); + + /* Reset Type & Flooding flag accordingly */ + lp->type = STD_TE; + UNSET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS); +} + +void set_linkparams_llri(struct mpls_te_link *lp, uint32_t local, + uint32_t remote) +{ + + lp->llri.header.type = htons(TE_LINK_SUBTLV_LLRI); + lp->llri.header.length = htons(TE_LINK_SUBTLV_LLRI_SIZE); + lp->llri.local = htonl(local); + lp->llri.remote = htonl(remote); +} + +void set_linkparams_lrrid(struct mpls_te_link *lp, struct in_addr local, + struct in_addr remote) +{ + + lp->lrrid.header.type = htons(TE_LINK_SUBTLV_LRRID); + lp->lrrid.header.length = htons(TE_LINK_SUBTLV_LRRID_SIZE); + lp->lrrid.local.s_addr = local.s_addr; + lp->lrrid.remote.s_addr = remote.s_addr; +} + +static void set_linkparams_av_delay(struct mpls_te_link *lp, uint32_t delay, + uint8_t anormal) +{ + uint32_t tmp; + /* Note that TLV-length field is the size of array. */ + lp->av_delay.header.type = htons(TE_LINK_SUBTLV_AV_DELAY); + lp->av_delay.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); + tmp = delay & TE_EXT_MASK; + if (anormal) + tmp |= TE_EXT_ANORMAL; + lp->av_delay.value = htonl(tmp); + return; +} + +static void set_linkparams_mm_delay(struct mpls_te_link *lp, uint32_t low, + uint32_t high, uint8_t anormal) +{ + uint32_t tmp; + /* Note that TLV-length field is the size of array. */ + lp->mm_delay.header.type = htons(TE_LINK_SUBTLV_MM_DELAY); + lp->mm_delay.header.length = htons(TE_LINK_SUBTLV_MM_DELAY_SIZE); + tmp = low & TE_EXT_MASK; + if (anormal) + tmp |= TE_EXT_ANORMAL; + lp->mm_delay.low = htonl(tmp); + lp->mm_delay.high = htonl(high); + return; +} + +static void set_linkparams_delay_var(struct mpls_te_link *lp, uint32_t jitter) +{ + /* Note that TLV-length field is the size of array. */ + lp->delay_var.header.type = htons(TE_LINK_SUBTLV_DELAY_VAR); + lp->delay_var.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); + lp->delay_var.value = htonl(jitter & TE_EXT_MASK); + return; +} + +static void set_linkparams_pkt_loss(struct mpls_te_link *lp, uint32_t loss, + uint8_t anormal) +{ + uint32_t tmp; + /* Note that TLV-length field is the size of array. */ + lp->pkt_loss.header.type = htons(TE_LINK_SUBTLV_PKT_LOSS); + lp->pkt_loss.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); + tmp = loss & TE_EXT_MASK; + if (anormal) + tmp |= TE_EXT_ANORMAL; + lp->pkt_loss.value = htonl(tmp); + return; +} + +static void set_linkparams_res_bw(struct mpls_te_link *lp, float fp) +{ + /* Note that TLV-length field is the size of array. */ + lp->res_bw.header.type = htons(TE_LINK_SUBTLV_RES_BW); + lp->res_bw.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); + lp->res_bw.value = htonf(fp); + return; +} + +static void set_linkparams_ava_bw(struct mpls_te_link *lp, float fp) +{ + /* Note that TLV-length field is the size of array. */ + lp->ava_bw.header.type = htons(TE_LINK_SUBTLV_AVA_BW); + lp->ava_bw.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); + lp->ava_bw.value = htonf(fp); + return; +} + +static void set_linkparams_use_bw(struct mpls_te_link *lp, float fp) +{ + /* Note that TLV-length field is the size of array. */ + lp->use_bw.header.type = htons(TE_LINK_SUBTLV_USE_BW); + lp->use_bw.header.length = htons(TE_LINK_SUBTLV_DEF_SIZE); + lp->use_bw.value = htonf(fp); + return; +} + +/* Update TE parameters from Interface */ +static void update_linkparams(struct mpls_te_link *lp) +{ + int i; + struct interface *ifp; + + /* Get the Interface structure */ + if ((ifp = lp->ifp) == NULL) { + ote_debug( + "MPLS-TE (%s): Abort update TE parameters: no interface associated to Link Parameters", + __func__); + return; + } + if (!HAS_LINK_PARAMS(ifp)) { + ote_debug( + "MPLS-TE (%s): Abort update TE parameters: no Link Parameters for interface", + __func__); + return; + } + + /* RFC3630 metrics */ + if (IS_PARAM_SET(ifp->link_params, LP_ADM_GRP)) + set_linkparams_rsc_clsclr(lp, ifp->link_params->admin_grp); + else + TLV_TYPE(lp->rsc_clsclr) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_MAX_BW)) + set_linkparams_max_bw(lp, ifp->link_params->max_bw); + else + TLV_TYPE(lp->max_bw) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_MAX_RSV_BW)) + set_linkparams_max_rsv_bw(lp, ifp->link_params->max_rsv_bw); + else + TLV_TYPE(lp->max_rsv_bw) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_UNRSV_BW)) + for (i = 0; i < MAX_CLASS_TYPE; i++) + set_linkparams_unrsv_bw(lp, i, + ifp->link_params->unrsv_bw[i]); + else + TLV_TYPE(lp->unrsv_bw) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_TE_METRIC)) + set_linkparams_te_metric(lp, ifp->link_params->te_metric); + else + TLV_TYPE(lp->te_metric) = 0; + + /* TE metric Extensions */ + if (IS_PARAM_SET(ifp->link_params, LP_DELAY)) + set_linkparams_av_delay(lp, ifp->link_params->av_delay, 0); + else + TLV_TYPE(lp->av_delay) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_MM_DELAY)) + set_linkparams_mm_delay(lp, ifp->link_params->min_delay, + ifp->link_params->max_delay, 0); + else + TLV_TYPE(lp->mm_delay) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_DELAY_VAR)) + set_linkparams_delay_var(lp, ifp->link_params->delay_var); + else + TLV_TYPE(lp->delay_var) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_PKT_LOSS)) + set_linkparams_pkt_loss(lp, ifp->link_params->pkt_loss, 0); + else + TLV_TYPE(lp->pkt_loss) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_RES_BW)) + set_linkparams_res_bw(lp, ifp->link_params->res_bw); + else + TLV_TYPE(lp->res_bw) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_AVA_BW)) + set_linkparams_ava_bw(lp, ifp->link_params->ava_bw); + else + TLV_TYPE(lp->ava_bw) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_USE_BW)) + set_linkparams_use_bw(lp, ifp->link_params->use_bw); + else + TLV_TYPE(lp->use_bw) = 0; + + /* RFC5392 */ + if (IS_PARAM_SET(ifp->link_params, LP_RMT_AS)) { + /* Flush LSA if it engaged and was previously a STD_TE one */ + if (IS_STD_TE(lp->type) + && CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) { + ote_debug( + "MPLS-TE (%s): Update IF: Switch from Standard LSA to INTER-AS for %s[%d/%d]", + __func__, ifp->name, lp->flags, lp->type); + + ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); + /* Then, switch it to INTER-AS */ + if (OspfMplsTE.inter_as == AS) { + lp->type = INTER_AS; + SET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS); + } else { + lp->type = INTER_AS; + UNSET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS); + lp->area = ospf_area_lookup_by_area_id( + ospf_lookup_by_vrf_id(VRF_DEFAULT), + OspfMplsTE.interas_areaid); + } + } + set_linkparams_inter_as(lp, ifp->link_params->rmt_ip, + ifp->link_params->rmt_as); + } else { + ote_debug( + "MPLS-TE (%s): Update IF: Switch from INTER-AS LSA to Standard for %s[%d/%d]", + __func__, ifp->name, lp->flags, lp->type); + + /* reset inter-as TE params */ + /* Flush LSA if it engaged and was previously an INTER_AS one */ + if (IS_INTER_AS(lp->type) + && CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) { + ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); + /* Then, switch it to Standard TE */ + lp->flags = STD_TE; + UNSET_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS); + } + unset_linkparams_inter_as(lp); + } +} + +static void initialize_linkparams(struct mpls_te_link *lp) +{ + struct interface *ifp = lp->ifp; + struct ospf_interface *oi = NULL; + struct route_node *rn; + + ote_debug("MPLS-TE (%s): Initialize Link Parameters for interface %s", + __func__, ifp->name); + + /* Search OSPF Interface parameters for this interface */ + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + + if ((oi = rn->info) == NULL) + continue; + + if (oi->ifp == ifp) + break; + } + + if ((oi == NULL) || (oi->ifp != ifp)) { + ote_debug( + "MPLS-TE (%s): Could not find corresponding OSPF Interface for %s", + __func__, ifp->name); + return; + } + + /* + * Try to set initial values those can be derived from + * zebra-interface information. + */ + set_linkparams_link_type(oi, lp); + + /* Set local IP addr */ + set_linkparams_lclif_ipaddr(lp, oi->address->u.prefix4); + + /* Set Remote IP addr if Point to Point Interface */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT) { + struct prefix *pref = CONNECTED_PREFIX(oi->connected); + if (pref != NULL) + set_linkparams_rmtif_ipaddr(lp, pref->u.prefix4); + } + + /* Keep Area information in combination with link parameters. */ + lp->area = oi->area; + + return; +} + +static int is_mandated_params_set(struct mpls_te_link *lp) +{ + int rc = 0; + + if (ntohs(OspfMplsTE.router_addr.header.type) == 0) { + flog_warn(EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): Missing Router Address", __func__); + return rc; + } + + if (ntohs(lp->link_type.header.type) == 0) { + flog_warn(EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): Missing Link Type", __func__); + return rc; + } + + if (!IS_INTER_AS(lp->type) && (ntohs(lp->link_id.header.type) == 0)) { + flog_warn(EC_OSPF_TE_UNEXPECTED, "MPLS-TE (%s) Missing Link ID", + __func__); + return rc; + } + + rc = 1; + return rc; +} + +/*------------------------------------------------------------------------* + * Following are callback functions against generic Opaque-LSAs handling. + *------------------------------------------------------------------------*/ + +static int ospf_mpls_te_new_if(struct interface *ifp) +{ + struct mpls_te_link *new; + + ote_debug("MPLS-TE (%s): Add new %s interface %s to MPLS-TE list", + __func__, ifp->link_params ? "Active" : "Inactive", + ifp->name); + + if (lookup_linkparams_by_ifp(ifp) != NULL) + return 0; + + new = XCALLOC(MTYPE_OSPF_MPLS_TE, sizeof(struct mpls_te_link)); + + new->instance = get_mpls_te_instance_value(); + new->ifp = ifp; + /* By default TE-Link is RFC3630 compatible flooding in Area and not + * active */ + /* This default behavior will be adapted with call to + * ospf_mpls_te_update_if() */ + new->type = STD_TE; + new->flags = LPFLG_LSA_INACTIVE; + + /* Initialize Link Parameters from Interface */ + initialize_linkparams(new); + + /* Set TE Parameters from Interface */ + update_linkparams(new); + + /* Add Link Parameters structure to the list */ + listnode_add(OspfMplsTE.iflist, new); + + ote_debug("MPLS-TE (%s): Add new LP context for %s[%d/%d]", __func__, + ifp->name, new->flags, new->type); + + /* Schedule Opaque-LSA refresh. */ /* XXX */ + return 0; +} + +static int ospf_mpls_te_del_if(struct interface *ifp) +{ + struct mpls_te_link *lp; + int rc = -1; + + if ((lp = lookup_linkparams_by_ifp(ifp)) != NULL) { + struct list *iflist = OspfMplsTE.iflist; + + /* Dequeue listnode entry from the list. */ + listnode_delete(iflist, lp); + + XFREE(MTYPE_OSPF_MPLS_TE, lp); + } + + /* Schedule Opaque-LSA refresh. */ /* XXX */ + + rc = 0; + return rc; +} + +/* Main initialization / update function of the MPLS TE Link context */ + +/* Call when interface TE Link parameters are modified */ +void ospf_mpls_te_update_if(struct interface *ifp) +{ + struct mpls_te_link *lp; + + ote_debug("MPLS-TE (%s): Update LSA parameters for interface %s [%s]", + __func__, ifp->name, HAS_LINK_PARAMS(ifp) ? "ON" : "OFF"); + + /* Get Link context from interface */ + if ((lp = lookup_linkparams_by_ifp(ifp)) == NULL) { + flog_warn( + EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): Did not find Link Parameters context for interface %s", + __func__, ifp->name); + return; + } + + /* Fulfill MPLS-TE Link TLV from Interface TE Link parameters */ + if (HAS_LINK_PARAMS(ifp)) { + SET_FLAG(lp->flags, LPFLG_LSA_ACTIVE); + + /* Update TE parameters */ + update_linkparams(lp); + + /* Finally Re-Originate or Refresh Opaque LSA if MPLS_TE is + * enabled */ + if (OspfMplsTE.enabled) + if (lp->area != NULL) { + if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) + ospf_mpls_te_lsa_schedule( + lp, REFRESH_THIS_LSA); + else + ospf_mpls_te_lsa_schedule( + lp, REORIGINATE_THIS_LSA); + } + } else { + /* If MPLS TE is disable on this interface, flush LSA if it is + * already engaged */ + if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) + ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); + else + /* Reset Activity flag */ + lp->flags = LPFLG_LSA_INACTIVE; + } + + return; +} + +/* + * Just add interface and set available information. Other information + * and flooding of LSA will be done later when adjacency will be up + * See ospf_mpls_te_nsm_change() after + */ +static void ospf_mpls_te_ism_change(struct ospf_interface *oi, int old_state) +{ + + struct mpls_te_link *lp; + + lp = lookup_linkparams_by_ifp(oi->ifp); + if (lp == NULL) { + flog_warn( + EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): Cannot get linkparams from OI(%s)?", + __func__, IF_NAME(oi)); + return; + } + + if (oi->area == NULL || oi->area->ospf == NULL) { + flog_warn( + EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): Cannot refer to OSPF from OI(%s)?", + __func__, IF_NAME(oi)); + return; + } + + /* Keep Area information in combination with linkparams. */ + lp->area = oi->area; + + switch (oi->state) { + case ISM_PointToPoint: + case ISM_DROther: + case ISM_Backup: + case ISM_DR: + /* Set Link type and Local IP addr */ + set_linkparams_link_type(oi, lp); + set_linkparams_lclif_ipaddr(lp, oi->address->u.prefix4); + + break; + case ISM_Down: + /* Interface goes Down: Flush LSA if engaged */ + if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) { + ote_debug( + "MPLS-TE (%s): Interface %s goes down: flush LSA", + __func__, IF_NAME(oi)); + ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); + return; + } + break; + default: + break; + } + + ote_debug("MPLS-TE (%s): Update Link parameters for interface %s", + __func__, IF_NAME(oi)); + + return; +} + +/* + * Complete TE info and schedule LSA flooding + * Link-ID and Remote IP address must be set with neighbor info + * which are only valid once NSM state is FULL + */ +static void ospf_mpls_te_nsm_change(struct ospf_neighbor *nbr, int old_state) +{ + struct ospf_interface *oi = nbr->oi; + struct mpls_te_link *lp; + + /* Process Neighbor only when its state is NSM Full */ + if (nbr->state != NSM_Full) + return; + + /* Get interface information for Traffic Engineering */ + lp = lookup_linkparams_by_ifp(oi->ifp); + if (lp == NULL) { + flog_warn( + EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): Cannot get linkparams from OI(%s)?", + __func__, IF_NAME(oi)); + return; + } + + if (oi->area == NULL || oi->area->ospf == NULL) { + flog_warn( + EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): Cannot refer to OSPF from OI(%s)?", + __func__, IF_NAME(oi)); + return; + } + + /* Flush TE Opaque LSA if Neighbor State goes Down or Deleted */ + if (OspfMplsTE.enabled + && (nbr->state == NSM_Down || nbr->state == NSM_Deleted)) { + if (CHECK_FLAG(lp->flags, EXT_LPFLG_LSA_ENGAGED)) { + ote_debug( + "MPLS-TE (%s): Interface %s goes down: flush LSA", + __func__, IF_NAME(oi)); + ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); + } + return; + } + + /* Keep Area information in combination with SR info. */ + lp->area = oi->area; + + /* + * The Link ID is identical to the contents of the Link ID field + * in the Router LSA for these link types. + */ + switch (oi->state) { + case ISM_PointToPoint: + /* Set Link ID with neighbor Router ID */ + set_linkparams_link_id(lp, nbr->router_id); + /* Set Remote IP address */ + set_linkparams_rmtif_ipaddr(lp, nbr->address.u.prefix4); + break; + + case ISM_DR: + case ISM_DROther: + case ISM_Backup: + /* Set Link ID with the Designated Router ID */ + set_linkparams_link_id(lp, DR(oi)); + break; + + case ISM_Down: + /* State goes Down: Flush LSA if engaged */ + if (OspfMplsTE.enabled + && CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) { + ote_debug( + "MPLS-TE (%s): Interface %s goes down: flush LSA", + __func__, IF_NAME(oi)); + ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); + } + return; + default: + break; + } + + ote_debug("MPLS-TE (%s): Add Link-ID %pI4 for interface %s ", __func__, + &lp->link_id.value, oi->ifp->name); + + /* Try to Schedule LSA */ + if (OspfMplsTE.enabled) { + if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) + ospf_mpls_te_lsa_schedule(lp, REFRESH_THIS_LSA); + else + ospf_mpls_te_lsa_schedule(lp, REORIGINATE_THIS_LSA); + } + return; +} + +/*------------------------------------------------------------------------* + * Following are OSPF protocol processing functions for MPLS-TE LSA. + *------------------------------------------------------------------------*/ + +static void build_tlv_header(struct stream *s, struct tlv_header *tlvh) +{ + stream_put(s, tlvh, sizeof(struct tlv_header)); + return; +} + +static void build_router_tlv(struct stream *s) +{ + struct tlv_header *tlvh = &OspfMplsTE.router_addr.header; + if (ntohs(tlvh->type) != 0) { + build_tlv_header(s, tlvh); + stream_put(s, TLV_DATA(tlvh), TLV_BODY_SIZE(tlvh)); + } + return; +} + +static void build_link_subtlv(struct stream *s, struct tlv_header *tlvh) +{ + + if ((tlvh != NULL) && (ntohs(tlvh->type) != 0)) { + build_tlv_header(s, tlvh); + stream_put(s, TLV_DATA(tlvh), TLV_BODY_SIZE(tlvh)); + } + return; +} + +static void build_link_tlv(struct stream *s, struct mpls_te_link *lp) +{ + set_linkparams_link_header(lp); + build_tlv_header(s, &lp->link_header.header); + + build_link_subtlv(s, &lp->link_type.header); + build_link_subtlv(s, &lp->link_id.header); + build_link_subtlv(s, &lp->lclif_ipaddr.header); + build_link_subtlv(s, &lp->rmtif_ipaddr.header); + build_link_subtlv(s, &lp->te_metric.header); + build_link_subtlv(s, &lp->max_bw.header); + build_link_subtlv(s, &lp->max_rsv_bw.header); + build_link_subtlv(s, &lp->unrsv_bw.header); + build_link_subtlv(s, &lp->rsc_clsclr.header); + build_link_subtlv(s, &lp->lrrid.header); + build_link_subtlv(s, &lp->llri.header); + build_link_subtlv(s, &lp->rip.header); + build_link_subtlv(s, &lp->ras.header); + build_link_subtlv(s, &lp->av_delay.header); + build_link_subtlv(s, &lp->mm_delay.header); + build_link_subtlv(s, &lp->delay_var.header); + build_link_subtlv(s, &lp->pkt_loss.header); + build_link_subtlv(s, &lp->res_bw.header); + build_link_subtlv(s, &lp->ava_bw.header); + build_link_subtlv(s, &lp->use_bw.header); + + return; +} + +static void ospf_mpls_te_lsa_body_set(struct stream *s, struct mpls_te_link *lp) +{ + /* + * The router address TLV is type 1, and ... It must appear in exactly + * one Traffic Engineering LSA originated by a router but not in + * Inter-AS TLV. + */ + if (!IS_INTER_AS(lp->type)) + build_router_tlv(s); + + /* + * Only one Link TLV shall be carried in each LSA, allowing for fine + * granularity changes in topology. + */ + build_link_tlv(s, lp); + return; +} + +/* Create new opaque-LSA. */ +static struct ospf_lsa *ospf_mpls_te_lsa_new(struct ospf *ospf, + struct ospf_area *area, + struct mpls_te_link *lp) +{ + struct stream *s; + struct lsa_header *lsah; + struct ospf_lsa *new = NULL; + uint8_t options, lsa_type = 0; + struct in_addr lsa_id; + uint32_t tmp; + uint16_t length; + + /* Create a stream for LSA. */ + s = stream_new(OSPF_MAX_LSA_SIZE); + lsah = (struct lsa_header *)STREAM_DATA(s); + + options = OSPF_OPTION_O; /* Don't forget this :-) */ + + /* Set opaque-LSA header fields depending of the type of RFC */ + if (IS_INTER_AS(lp->type)) { + if (IS_FLOOD_AS(lp->flags)) { + /* Enable AS external as we flood Inter-AS with Opaque + * Type 11 + */ + options |= OSPF_OPTION_E; + lsa_type = OSPF_OPAQUE_AS_LSA; + } else { + options |= LSA_OPTIONS_GET( + area); /* Get area default option */ + options |= LSA_OPTIONS_NSSA_GET(area); + lsa_type = OSPF_OPAQUE_AREA_LSA; + } + tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_INTER_AS_LSA, lp->instance); + lsa_id.s_addr = htonl(tmp); + + if (!ospf) { + stream_free(s); + return NULL; + } + + lsa_header_set(s, options, lsa_type, lsa_id, ospf->router_id); + } else { + options |= LSA_OPTIONS_GET(area); /* Get area default option */ + options |= LSA_OPTIONS_NSSA_GET(area); + lsa_type = OSPF_OPAQUE_AREA_LSA; + tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA, + lp->instance); + lsa_id.s_addr = htonl(tmp); + lsa_header_set(s, options, lsa_type, lsa_id, + area->ospf->router_id); + } + + ote_debug( + "MPLS-TE (%s): LSA[Type%d:%pI4]: Create an Opaque-LSA/MPLS-TE instance", + __func__, lsa_type, &lsa_id); + + /* Set opaque-LSA body fields. */ + ospf_mpls_te_lsa_body_set(s, lp); + + /* Set length. */ + length = stream_get_endp(s); + lsah->length = htons(length); + + /* Now, create an OSPF LSA instance. */ + new = ospf_lsa_new_and_data(length); + + new->area = area; + new->vrf_id = VRF_DEFAULT; + + SET_FLAG(new->flags, OSPF_LSA_SELF); + memcpy(new->data, lsah, length); + stream_free(s); + + return new; +} + +static int ospf_mpls_te_lsa_originate1(struct ospf_area *area, + struct mpls_te_link *lp) +{ + struct ospf_lsa *new = NULL; + int rc = -1; + + /* Create new Opaque-LSA/MPLS-TE instance. */ + new = ospf_mpls_te_lsa_new(area->ospf, area, lp); + if (new == NULL) { + flog_warn(EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): ospf_mpls_te_lsa_new() ?", __func__); + return rc; + } + + /* Install this LSA into LSDB. */ + if (ospf_lsa_install(area->ospf, NULL /*oi*/, new) == NULL) { + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "MPLS-TE (%s): ospf_lsa_install() ?", __func__); + ospf_lsa_unlock(&new); + return rc; + } + + /* Now this link-parameter entry has associated LSA. */ + SET_FLAG(lp->flags, LPFLG_LSA_ENGAGED); + /* Update new LSA origination count. */ + area->ospf->lsa_originate_count++; + + /* Flood new LSA through area. */ + ospf_flood_through_area(area, NULL /*nbr*/, new); + + ote_debug( + "MPLS-TE (%s): LSA[Type%d:%pI4]: Originate Opaque-LSA/MPLS-TE: Area(%pI4), Link(%s)", + __func__, new->data->type, &new->data->id, &area->area_id, + lp->ifp->name); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + ospf_lsa_header_dump(new->data); + + rc = 0; + return rc; +} + +static int ospf_mpls_te_lsa_originate_area(void *arg) +{ + struct ospf_area *area = (struct ospf_area *)arg; + struct listnode *node, *nnode; + struct mpls_te_link *lp; + int rc = -1; + + if (!OspfMplsTE.enabled) { + ote_debug("MPLS-TE (%s): MPLS-TE is disabled now.", __func__); + rc = 0; /* This is not an error case. */ + return rc; + } + + for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) { + /* Process only enabled LSA with area scope flooding */ + if (!CHECK_FLAG(lp->flags, LPFLG_LSA_ACTIVE) + || IS_FLOOD_AS(lp->flags)) + continue; + + if (lp->area == NULL) + continue; + + if (!IPV4_ADDR_SAME(&lp->area->area_id, &area->area_id)) + continue; + + if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) { + if (CHECK_FLAG(lp->flags, LPFLG_LSA_FORCED_REFRESH)) { + UNSET_FLAG(lp->flags, LPFLG_LSA_FORCED_REFRESH); + ote_debug( + "MPLS-TE (%s): Refresh instead of Originate", + __func__); + ospf_mpls_te_lsa_schedule(lp, REFRESH_THIS_LSA); + } + continue; + } + + if (!is_mandated_params_set(lp)) { + ote_debug( + "MPLS-TE (%s): Link(%s) lacks some mandated MPLS-TE parameters.", + __func__, lp->ifp ? lp->ifp->name : "?"); + continue; + } + + /* Ok, let's try to originate an LSA for this area and Link. */ + ote_debug( + "MPLS-TE (%s): Let's finally reoriginate the LSA %d through the Area %pI4 for Link %s", + __func__, lp->instance, &area->area_id, + lp->ifp ? lp->ifp->name : "?"); + if (ospf_mpls_te_lsa_originate1(area, lp) != 0) + return rc; + } + + rc = 0; + return rc; +} + +static int ospf_mpls_te_lsa_originate2(struct ospf *top, + struct mpls_te_link *lp) +{ + struct ospf_lsa *new; + int rc = -1; + + /* Create new Opaque-LSA/Inter-AS instance. */ + new = ospf_mpls_te_lsa_new(top, NULL, lp); + if (new == NULL) { + flog_warn(EC_OSPF_LSA_UNEXPECTED, + "MPLS-TE (%s): ospf_router_info_lsa_new() ?", + __func__); + return rc; + } + + /* Install this LSA into LSDB. */ + if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) { + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "MPLS-TE (%s): ospf_lsa_install() ?", __func__); + ospf_lsa_unlock(&new); + return rc; + } + + /* Now this Router Info parameter entry has associated LSA. */ + SET_FLAG(lp->flags, LPFLG_LSA_ENGAGED); + /* Update new LSA origination count. */ + top->lsa_originate_count++; + + /* Flood new LSA through AS. */ + ospf_flood_through_as(top, NULL /*nbr */, new); + + ote_debug( + "MPLS-TE (%s): LSA[Type%d:%pI4]: Originate Opaque-LSA/MPLS-TE Inter-AS", + __func__, new->data->type, &new->data->id); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + ospf_lsa_header_dump(new->data); + + + rc = 0; + return rc; +} + +static int ospf_mpls_te_lsa_originate_as(void *arg) +{ + struct ospf *top; + struct ospf_area *area; + struct listnode *node, *nnode; + struct mpls_te_link *lp; + int rc = -1; + + if ((!OspfMplsTE.enabled) || (OspfMplsTE.inter_as == Off)) { + ote_debug("MPLS-TE (%s): Inter-AS is disabled for now", + __func__); + rc = 0; /* This is not an error case. */ + return rc; + } + + for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) { + /* Process only enabled INTER_AS Links or Pseudo-Links */ + if (!CHECK_FLAG(lp->flags, LPFLG_LSA_ACTIVE) + || !CHECK_FLAG(lp->flags, LPFLG_LSA_FLOOD_AS) + || !IS_INTER_AS(lp->type)) + continue; + + if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) { + if (CHECK_FLAG(lp->flags, LPFLG_LSA_FORCED_REFRESH)) { + UNSET_FLAG(lp->flags, LPFLG_LSA_FORCED_REFRESH); + ospf_mpls_te_lsa_schedule(lp, REFRESH_THIS_LSA); + } + continue; + } + + if (!is_mandated_params_set(lp)) { + flog_warn( + EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): Link(%s) lacks some mandated MPLS-TE parameters.", + __func__, lp->ifp ? lp->ifp->name : "?"); + continue; + } + + /* Ok, let's try to originate an LSA for this AS and Link. */ + ote_debug( + "MPLS-TE (%s): Let's finally re-originate the Inter-AS LSA %d through the %s for Link %s", + __func__, lp->instance, + IS_FLOOD_AS(lp->flags) ? "AS" : "Area", + lp->ifp ? lp->ifp->name : "Unknown"); + + if (IS_FLOOD_AS(lp->flags)) { + top = (struct ospf *)arg; + ospf_mpls_te_lsa_originate2(top, lp); + } else { + area = (struct ospf_area *)arg; + ospf_mpls_te_lsa_originate1(area, lp); + } + } + + rc = 0; + return rc; +} + +/* + * As Inter-AS LSA must be registered with both AREA and AS flooding, and + * because all origination callback functions are call (disregarding the Opaque + * LSA type and Flooding scope) it is necessary to determine which flooding + * scope is associated with the LSA origination as parameter is of type void and + * must be cast to struct *ospf for AS flooding and to struct *ospf_area for + * Area flooding. + */ +static int ospf_mpls_te_lsa_inter_as_as(void *arg) +{ + if (OspfMplsTE.inter_as == AS) + return ospf_mpls_te_lsa_originate_as(arg); + else + return 0; +} + +static int ospf_mpls_te_lsa_inter_as_area(void *arg) +{ + if (OspfMplsTE.inter_as == Area) + return ospf_mpls_te_lsa_originate_area(arg); + else + return 0; +} + +static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa) +{ + struct mpls_te_link *lp; + struct ospf_area *area = lsa->area; + struct ospf *top; + struct ospf_lsa *new = NULL; + + if (!OspfMplsTE.enabled) { + /* + * This LSA must have flushed before due to MPLS-TE status + * change. + * It seems a slip among routers in the routing domain. + */ + ote_debug("MPLS-TE (%s): MPLS-TE is disabled now", __func__); + lsa->data->ls_age = + htons(OSPF_LSA_MAXAGE); /* Flush it anyway. */ + } + + /* At first, resolve lsa/lp relationship. */ + if ((lp = lookup_linkparams_by_instance(lsa)) == NULL) { + flog_warn(EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): Invalid parameter?", __func__); + lsa->data->ls_age = + htons(OSPF_LSA_MAXAGE); /* Flush it anyway. */ + ospf_opaque_lsa_flush_schedule(lsa); + return NULL; + } + + /* Check if lp was not disable in the interval */ + if (!CHECK_FLAG(lp->flags, LPFLG_LSA_ACTIVE)) { + flog_warn(EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): lp was disabled: Flush it!", __func__); + lsa->data->ls_age = + htons(OSPF_LSA_MAXAGE); /* Flush it anyway. */ + } + + /* If the lsa's age reached to MaxAge, start flushing procedure. */ + if (IS_LSA_MAXAGE(lsa)) { + UNSET_FLAG(lp->flags, LPFLG_LSA_ENGAGED); + ospf_opaque_lsa_flush_schedule(lsa); + return NULL; + } + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + /* Create new Opaque-LSA/MPLS-TE instance. */ + new = ospf_mpls_te_lsa_new(top, area, lp); + if (new == NULL) { + flog_warn(EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): ospf_mpls_te_lsa_new() ?", __func__); + return NULL; + } + new->data->ls_seqnum = lsa_seqnum_increment(lsa); + + /* Install this LSA into LSDB. */ + /* Given "lsa" will be freed in the next function. */ + /* As area could be NULL i.e. when using OPAQUE_LSA_AS, we prefer to use + * ospf_lookup() to get ospf instance */ + if (area) + top = area->ospf; + + if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) { + flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, + "MPLS-TE (%s): ospf_lsa_install() ?", __func__); + ospf_lsa_unlock(&new); + return NULL; + } + + /* Flood updated LSA through AS or Area depending of the RFC of the link + */ + if (IS_FLOOD_AS(lp->flags)) + ospf_flood_through_as(top, NULL, new); + else + ospf_flood_through_area(area, NULL /*nbr*/, new); + + /* Debug logging. */ + ote_debug("MPLS-TE (%s): LSA[Type%d:%pI4]: Refresh Opaque-LSA/MPLS-TE", + __func__, new->data->type, &new->data->id); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + ospf_lsa_header_dump(new->data); + + return new; +} + +void ospf_mpls_te_lsa_schedule(struct mpls_te_link *lp, enum lsa_opcode opcode) +{ + struct ospf_lsa lsa; + struct lsa_header lsah; + struct ospf *top; + uint32_t tmp; + + memset(&lsa, 0, sizeof(lsa)); + memset(&lsah, 0, sizeof(lsah)); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + + /* Check if the pseudo link is ready to flood */ + if (!CHECK_FLAG(lp->flags, LPFLG_LSA_ACTIVE)) + return; + + ote_debug("MPLS-TE (%s): Schedule %s%s%s LSA for interface %s", + __func__, + opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "", + opcode == REFRESH_THIS_LSA ? "Refresh" : "", + opcode == FLUSH_THIS_LSA ? "Flush" : "", + lp->ifp ? lp->ifp->name : "-"); + + lsa.area = lp->area; + lsa.data = &lsah; + if (IS_FLOOD_AS(lp->flags)) { + lsah.type = OSPF_OPAQUE_AS_LSA; + tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_INTER_AS_LSA, lp->instance); + lsah.id.s_addr = htonl(tmp); + } else { + lsah.type = OSPF_OPAQUE_AREA_LSA; + if (IS_INTER_AS(lp->type)) { + /* Set the area context if not know */ + if (lp->area == NULL) + lp->area = ospf_area_lookup_by_area_id( + top, OspfMplsTE.interas_areaid); + /* Unable to set the area context. Abort! */ + if (lp->area == NULL) { + flog_warn( + EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): Area context is null. Abort !", + __func__); + return; + } + tmp = SET_OPAQUE_LSID(OPAQUE_TYPE_INTER_AS_LSA, + lp->instance); + } else + tmp = SET_OPAQUE_LSID( + OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA, + lp->instance); + lsah.id.s_addr = htonl(tmp); + } + + switch (opcode) { + case REORIGINATE_THIS_LSA: + if (IS_FLOOD_AS(lp->flags)) { + ospf_opaque_lsa_reoriginate_schedule( + (void *)top, OSPF_OPAQUE_AS_LSA, + OPAQUE_TYPE_INTER_AS_LSA); + } else { + if (IS_INTER_AS(lp->type)) + ospf_opaque_lsa_reoriginate_schedule( + (void *)lp->area, OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_INTER_AS_LSA); + else + ospf_opaque_lsa_reoriginate_schedule( + (void *)lp->area, OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA); + } + break; + case REFRESH_THIS_LSA: + ospf_opaque_lsa_refresh_schedule(&lsa); + break; + case FLUSH_THIS_LSA: + /* Reset Activity flag */ + lp->flags = LPFLG_LSA_INACTIVE; + ospf_opaque_lsa_flush_schedule(&lsa); + break; + default: + flog_warn(EC_OSPF_TE_UNEXPECTED, + "MPLS-TE (%s): Unknown opcode (%u)", __func__, + opcode); + break; + } +} + +/** + * ------------------------------------------------------ + * Following are Link State Data Base control functions. + * ------------------------------------------------------ + */ + +/** + * Get Vertex from TED by the router which advertised the LSA. A new Vertex and + * associated Link State Node are created if Vertex is not found. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return Link State Vertex + */ +static struct ls_vertex *get_vertex(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_node_id lnid; + struct ls_node *lnode; + struct ls_vertex *vertex; + + /* Sanity Check */ + if (!ted || !lsa || !lsa->data || !lsa->area) + return NULL; + + /* Search if a Link State Vertex already exist */ + lnid.origin = OSPFv2; + lnid.id.ip.addr = lsa->data->adv_router; + lnid.id.ip.area_id = lsa->area->area_id; + vertex = ls_find_vertex_by_id(ted, lnid); + + /* Create Node & Vertex in the Link State Date Base if not found */ + if (!vertex) { + const struct in_addr inaddr_any = {.s_addr = INADDR_ANY}; + + lnode = ls_node_new(lnid, inaddr_any, in6addr_any); + snprintfrr(lnode->name, MAX_NAME_LENGTH, "%pI4", + &lnid.id.ip.addr); + vertex = ls_vertex_add(ted, lnode); + } + + if (IS_LSA_SELF(lsa)) + ted->self = vertex; + + return vertex; +} + +/** + * Get Edge from TED by Link State Attribute ID. A new Edge and associated Link + * State Attributes are created if not found. + * + * @param ted Link State Traffic Engineering Database + * @param adv Link State Node ID of router which advertised Edge + * @param link_id Link State Attribute ID + * + * @return Link State Edge + */ +static struct ls_edge *get_edge(struct ls_ted *ted, struct ls_node_id adv, + struct in_addr link_id) +{ + struct ls_edge_key key; + struct ls_edge *edge; + struct ls_attributes *attr; + + /* Search Edge that corresponds to the Link ID */ + key.family = AF_INET; + IPV4_ADDR_COPY(&key.k.addr, &link_id); + edge = ls_find_edge_by_key(ted, key); + + /* Create new one if not exist */ + if (!edge) { + attr = ls_attributes_new(adv, link_id, in6addr_any, 0); + edge = ls_edge_add(ted, attr); + } + + return edge; +} + +/** + * Export Link State information to consumer daemon through ZAPI Link State + * Opaque Message. + * + * @param type Type of Link State Element i.e. Vertex, Edge or Subnet + * @param link_state Pointer to Link State Vertex, Edge or Subnet + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_export(uint8_t type, void *link_state) +{ + struct ls_message msg = {}; + int rc = 0; + + if (!OspfMplsTE.export) + return rc; + + switch (type) { + case LS_MSG_TYPE_NODE: + ls_vertex2msg(&msg, (struct ls_vertex *)link_state); + rc = ls_send_msg(zclient, &msg, NULL); + break; + case LS_MSG_TYPE_ATTRIBUTES: + ls_edge2msg(&msg, (struct ls_edge *)link_state); + rc = ls_send_msg(zclient, &msg, NULL); + break; + case LS_MSG_TYPE_PREFIX: + ls_subnet2msg(&msg, (struct ls_subnet *)link_state); + rc = ls_send_msg(zclient, &msg, NULL); + break; + default: + rc = -1; + break; + } + + return rc; +} + +/** + * Update Link State Edge & Attributes from the given Link State Attributes ID + * and metric. This function is called when parsing Router LSA. + * + * @param ted Link State Traffic Engineering Database + * @param vertex Vertex where the Edge is attached as source + * @param link_data Link State Edge ID + * @param metric Standard metric attached to this Edge + */ +static void ospf_te_update_link(struct ls_ted *ted, struct ls_vertex *vertex, + struct in_addr link_data, uint8_t metric) +{ + struct ls_edge *edge; + struct ls_attributes *attr; + + /* Sanity check */ + if (!ted || !vertex || !vertex->node) + return; + + /* Get Corresponding Edge from Link State Data Base */ + edge = get_edge(ted, vertex->node->adv, link_data); + attr = edge->attributes; + + /* re-attached edge to vertex if needed */ + if (!edge->source) + edge->source = vertex; + + /* Check if it is just an LSA refresh */ + if ((CHECK_FLAG(attr->flags, LS_ATTR_METRIC) + && (attr->metric == metric))) { + edge->status = SYNC; + return; + } + + /* Update metric value */ + attr->metric = metric; + SET_FLAG(attr->flags, LS_ATTR_METRIC); + if (edge->status != NEW) + edge->status = UPDATE; + + ote_debug(" |- %s Edge %pI4 with metric %d", + edge->status == NEW ? "Add" : "Update", &attr->standard.local, + attr->metric); + + /* Export Link State Edge */ + ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); + edge->status = SYNC; +} + +/** + * Update Link State Subnet & Prefix from the given prefix and metric. This + * function is called when parsing Router LSA. + * + * @param ted Link State Traffic Engineering Database + * @param vertex Vertex where the Edge is attached as source + * @param p Prefix associated to the Subnet + * @param metric Standard metric attached to this Edge + */ +static void ospf_te_update_subnet(struct ls_ted *ted, struct ls_vertex *vertex, + struct prefix *p, uint8_t metric) +{ + struct ls_subnet *subnet; + struct ls_prefix *ls_pref; + + /* Search if there is a Subnet for this prefix */ + subnet = ls_find_subnet(ted, p); + + /* If found a Subnet, check if it is attached to this Vertex */ + if (subnet) { + /* Re-attach the subnet to the vertex if necessary */ + if (subnet->vertex != vertex) { + subnet->vertex = vertex; + listnode_add_sort_nodup(vertex->prefixes, subnet); + } + /* Check if it is a simple refresh */ + ls_pref = subnet->ls_pref; + if ((CHECK_FLAG(ls_pref->flags, LS_PREF_METRIC)) + && (ls_pref->metric == metric)) { + subnet->status = SYNC; + return; + } + ls_pref->metric = metric; + SET_FLAG(ls_pref->flags, LS_PREF_METRIC); + subnet->status = UPDATE; + } else { + /* Create new Link State Prefix */ + ls_pref = ls_prefix_new(vertex->node->adv, p); + ls_pref->metric = metric; + SET_FLAG(ls_pref->flags, LS_PREF_METRIC); + /* and add it to the TED */ + subnet = ls_subnet_add(ted, ls_pref); + } + + ote_debug(" |- %s subnet %pFX with metric %d", + subnet->status == NEW ? "Add" : "Update", &subnet->key, + ls_pref->metric); + + /* Export Link State Subnet */ + ospf_te_export(LS_MSG_TYPE_PREFIX, subnet); + subnet->status = SYNC; +} + +/** + * Delete Subnet that correspond to the given IPv4 address and export deletion + * information before removal. Prefix length is fixed to IPV4_MAX_BITLEN. + * + * @param ted Links State Database + * @param addr IPv4 address + */ +static void ospf_te_delete_subnet(struct ls_ted *ted, struct in_addr addr) +{ + struct prefix p; + struct ls_subnet *subnet; + + /* Search subnet that correspond to the address/32 as prefix */ + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = addr; + subnet = ls_find_subnet(ted, &p); + + /* Remove subnet if found */ + if (subnet) { + subnet->status = DELETE; + ospf_te_export(LS_MSG_TYPE_PREFIX, subnet); + ls_subnet_del_all(ted, subnet); + } +} + +/** + * Parse Router LSA. This function will create or update corresponding Vertex, + * Edge and Subnet. It also remove Edge and Subnet if they are marked as Orphan + * once Router LSA is parsed. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_parse_router_lsa(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct router_lsa *rl; + enum ls_node_type type; + struct ls_vertex *vertex; + struct ls_edge *edge; + struct ls_subnet *subnet; + struct listnode *node; + int len, links; + + /* Sanity Check */ + if (!ted || !lsa || !lsa->data) + return -1; + + ote_debug("MPLS-TE (%s): Parse Router LSA[%pI4] from Router[%pI4]", + __func__, &lsa->data->id, &lsa->data->adv_router); + + /* Get vertex from LSA Advertise Router ID */ + vertex = get_vertex(ted, lsa); + + /* Set Node type information if it has changed */ + rl = (struct router_lsa *)lsa->data; + if (IS_ROUTER_LSA_VIRTUAL(rl)) + type = PSEUDO; + else if (IS_ROUTER_LSA_EXTERNAL(rl)) + type = ASBR; + else if (IS_ROUTER_LSA_BORDER(rl)) + type = ABR; + else + type = STANDARD; + + if (vertex->status == NEW) { + vertex->node->type = type; + SET_FLAG(vertex->node->flags, LS_NODE_TYPE); + } else if (vertex->node->type != type) { + vertex->node->type = type; + vertex->status = UPDATE; + } + + /* Check if Vertex has been modified */ + if (vertex->status != SYNC) { + ote_debug(" |- %s Vertex %pI4", + vertex->status == NEW ? "Add" : "Update", + &vertex->node->router_id); + + /* Vertex is out of sync: export it */ + ospf_te_export(LS_MSG_TYPE_NODE, vertex); + vertex->status = SYNC; + } + + /* Mark outgoing Edge and Subnet as ORPHAN to detect deletion */ + for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, node, edge)) + edge->status = ORPHAN; + + for (ALL_LIST_ELEMENTS_RO(vertex->prefixes, node, subnet)) + subnet->status = ORPHAN; + + /* Then, process Link Information */ + len = lsa->size - OSPF_LSA_HEADER_SIZE - OSPF_ROUTER_LSA_MIN_SIZE; + links = ntohs(rl->links); + for (int i = 0; i < links && len > 0; len -= 12, i++) { + struct prefix p; + uint32_t metric; + + switch (rl->link[i].type) { + case LSA_LINK_TYPE_POINTOPOINT: + ospf_te_update_link(ted, vertex, rl->link[i].link_data, + ntohs(rl->link[i].metric)); + /* Add corresponding subnet */ + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = rl->link[i].link_data; + metric = ntohs(rl->link[i].metric); + ospf_te_update_subnet(ted, vertex, &p, metric); + break; + case LSA_LINK_TYPE_STUB: + /* Keep only /32 prefix */ + p.prefixlen = ip_masklen(rl->link[i].link_data); + if (p.prefixlen == IPV4_MAX_BITLEN) { + p.family = AF_INET; + p.u.prefix4 = rl->link[i].link_id; + metric = ntohs(rl->link[i].metric); + ospf_te_update_subnet(ted, vertex, &p, metric); + } + break; + default: + break; + } + } + /* Clean remaining Orphan Edges or Subnets */ + if (OspfMplsTE.export) + ls_vertex_clean(ted, vertex, zclient); + else + ls_vertex_clean(ted, vertex, NULL); + + return 0; +} + +/** + * Delete Vertex, Edge and Subnet associated to this Router LSA. This function + * is called when the router received such LSA with MAX_AGE (Flush) or when the + * router stop OSPF. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_delete_router_lsa(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_node_id lnid; + struct ls_vertex *vertex; + + /* Sanity Check */ + if (!ted || !lsa || !lsa->data) + return -1; + + /* Search Vertex that corresponds to this LSA */ + lnid.origin = OSPFv2; + lnid.id.ip.addr = lsa->data->adv_router; + lnid.id.ip.area_id = lsa->area->area_id; + vertex = ls_find_vertex_by_id(ted, lnid); + if (!vertex) + return -1; + + ote_debug("MPLS-TE (%s): Delete Vertex %pI4 from Router LSA[%pI4]", + __func__, &vertex->node->router_id, &lsa->data->id); + + /* Export deleted vertex ... */ + vertex->status = DELETE; + ospf_te_export(LS_MSG_TYPE_NODE, vertex); + + /* ... and remove Node & Vertex from Link State Date Base */ + ls_vertex_del_all(ted, vertex); + + return 0; +} + +/** + * Create or update Remote Vertex that corresponds to the remote ASBR of the + * foreign network if Edge is associated to an Inter-AS LSA (Type 6). + * + * @param ted Link State Traffic Engineering Database + * @param edge Link State Edge + */ +static void ospf_te_update_remote_asbr(struct ls_ted *ted, struct ls_edge *edge) +{ + struct ls_node_id lnid; + struct ls_vertex *vertex; + struct ls_node *lnode; + struct ls_attributes *attr; + struct prefix p; + + /* Sanity Check */ + if (!ted || !edge) + return; + + /* Search if a Link State Vertex already exist */ + attr = edge->attributes; + lnid.origin = OSPFv2; + lnid.id.ip.addr = attr->standard.remote_addr; + lnid.id.ip.area_id = attr->adv.id.ip.area_id; + vertex = ls_find_vertex_by_id(ted, lnid); + + /* Create Node & Vertex in the Link State Date Base if not found */ + if (!vertex) { + const struct in_addr inaddr_any = {.s_addr = INADDR_ANY}; + + lnode = ls_node_new(lnid, inaddr_any, in6addr_any); + snprintfrr(lnode->name, MAX_NAME_LENGTH, "%pI4", + &lnid.id.ip.addr); + vertex = ls_vertex_add(ted, lnode); + } + + /* Update Node information */ + lnode = vertex->node; + if (CHECK_FLAG(lnode->flags, LS_NODE_TYPE)) { + if (lnode->type != RMT_ASBR) { + lnode->type = RMT_ASBR; + if (vertex->status != NEW) + vertex->status = UPDATE; + } + } else { + lnode->type = RMT_ASBR; + SET_FLAG(lnode->flags, LS_NODE_TYPE); + if (vertex->status != NEW) + vertex->status = UPDATE; + } + if (CHECK_FLAG(lnode->flags, LS_NODE_AS_NUMBER)) { + if (lnode->as_number != attr->standard.remote_as) { + lnode->as_number = attr->standard.remote_as; + if (vertex->status != NEW) + vertex->status = UPDATE; + } + } else { + lnode->as_number = attr->standard.remote_as; + SET_FLAG(lnode->flags, LS_NODE_AS_NUMBER); + if (vertex->status != NEW) + vertex->status = UPDATE; + } + + /* Export Link State Vertex if needed */ + if (vertex->status == NEW || vertex->status == UPDATE) { + ote_debug(" |- %s Remote Vertex %pI4 for AS %u", + vertex->status == NEW ? "Add" : "Update", + &lnode->router_id, lnode->as_number); + ospf_te_export(LS_MSG_TYPE_NODE, vertex); + vertex->status = SYNC; + } + + /* Update corresponding Subnets */ + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = attr->standard.local; + ospf_te_update_subnet(ted, edge->source, &p, attr->standard.te_metric); + + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = attr->standard.remote_addr; + ospf_te_update_subnet(ted, vertex, &p, attr->standard.te_metric); + + /* Connect Edge to the remote Vertex */ + if (edge->destination == NULL) { + edge->destination = vertex; + listnode_add_sort_nodup(vertex->incoming_edges, edge); + } + + /* Finally set type to ASBR the node that advertised this Edge ... */ + vertex = edge->source; + lnode = vertex->node; + if (CHECK_FLAG(lnode->flags, LS_NODE_TYPE)) { + if (lnode->type != ASBR) { + lnode->type = ASBR; + if (vertex->status != NEW) + vertex->status = UPDATE; + } + } else { + lnode->type = ASBR; + SET_FLAG(lnode->flags, LS_NODE_TYPE); + if (vertex->status != NEW) + vertex->status = UPDATE; + } + + /* ... and Export it if needed */ + if (vertex->status == NEW || vertex->status == UPDATE) { + ospf_te_export(LS_MSG_TYPE_NODE, vertex); + vertex->status = SYNC; + } +} + +/** + * Parse Opaque Traffic Engineering LSA (Type 1) TLVs and create or update the + * corresponding Link State Edge and Attributes. Vertex connections are also + * updated if needed based on the remote IP address of the Edge and existing + * reverse Edge. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_parse_te(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_edge *edge; + struct ls_vertex *vertex; + struct ls_attributes *old, attr = {}; + struct tlv_header *tlvh; + void *value; + uint16_t len, sum; + uint8_t lsa_id; + + /* Initialize Attribute */ + attr.adv.origin = OSPFv2; + attr.adv.id.ip.addr = lsa->data->adv_router; + if (lsa->data->type != OSPF_OPAQUE_AS_LSA) + attr.adv.id.ip.area_id = lsa->area->area_id; + + /* Initialize TLV browsing */ + tlvh = TLV_HDR_TOP(lsa->data); + len = lsa->size - OSPF_LSA_HEADER_SIZE; + + /* Check if TE Router-ID TLV is present */ + if (ntohs(tlvh->type) == TE_TLV_ROUTER_ADDR) { + /* if TE Router-ID is alone, we are done ... */ + if (len == TE_LINK_SUBTLV_DEF_SIZE) + return 0; + + /* ... otherwise, skip it */ + len -= TE_LINK_SUBTLV_DEF_SIZE + TLV_HDR_SIZE; + tlvh = TLV_HDR_NEXT(tlvh); + } + + /* Check if we have a valid TE Link TLV */ + if ((len == 0) || (ntohs(tlvh->type) != TE_TLV_LINK)) + return 0; + + sum = sizeof(struct tlv_header); + /* Browse sub-TLV and fulfill Link State Attributes */ + for (tlvh = TLV_DATA(tlvh); sum < len; tlvh = TLV_HDR_NEXT(tlvh)) { + uint32_t val32, tab32[2]; + float valf, tabf[8]; + struct in_addr addr; + + value = TLV_DATA(tlvh); + switch (ntohs(tlvh->type)) { + case TE_LINK_SUBTLV_LCLIF_IPADDR: + memcpy(&addr, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.standard.local = addr; + SET_FLAG(attr.flags, LS_ATTR_LOCAL_ADDR); + break; + case TE_LINK_SUBTLV_RMTIF_IPADDR: + memcpy(&addr, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.standard.remote = addr; + SET_FLAG(attr.flags, LS_ATTR_NEIGH_ADDR); + break; + case TE_LINK_SUBTLV_TE_METRIC: + memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.standard.te_metric = ntohl(val32); + SET_FLAG(attr.flags, LS_ATTR_TE_METRIC); + break; + case TE_LINK_SUBTLV_MAX_BW: + memcpy(&valf, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.standard.max_bw = ntohf(valf); + SET_FLAG(attr.flags, LS_ATTR_MAX_BW); + break; + case TE_LINK_SUBTLV_MAX_RSV_BW: + memcpy(&valf, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.standard.max_rsv_bw = ntohf(valf); + SET_FLAG(attr.flags, LS_ATTR_MAX_RSV_BW); + break; + case TE_LINK_SUBTLV_UNRSV_BW: + memcpy(tabf, value, TE_LINK_SUBTLV_UNRSV_SIZE); + for (int i = 0; i < MAX_CLASS_TYPE; i++) + attr.standard.unrsv_bw[i] = ntohf(tabf[i]); + SET_FLAG(attr.flags, LS_ATTR_UNRSV_BW); + break; + case TE_LINK_SUBTLV_RSC_CLSCLR: + memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.standard.admin_group = ntohl(val32); + SET_FLAG(attr.flags, LS_ATTR_ADM_GRP); + break; + case TE_LINK_SUBTLV_LLRI: + memcpy(tab32, value, TE_LINK_SUBTLV_LLRI_SIZE); + attr.standard.local_id = ntohl(tab32[0]); + attr.standard.remote_id = ntohl(tab32[1]); + SET_FLAG(attr.flags, LS_ATTR_LOCAL_ID); + SET_FLAG(attr.flags, LS_ATTR_NEIGH_ID); + break; + case TE_LINK_SUBTLV_RIP: + memcpy(&addr, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.standard.remote_addr = addr; + SET_FLAG(attr.flags, LS_ATTR_REMOTE_ADDR); + break; + case TE_LINK_SUBTLV_RAS: + memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.standard.remote_as = ntohl(val32); + SET_FLAG(attr.flags, LS_ATTR_REMOTE_AS); + break; + case TE_LINK_SUBTLV_AV_DELAY: + memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.extended.delay = ntohl(val32); + SET_FLAG(attr.flags, LS_ATTR_DELAY); + break; + case TE_LINK_SUBTLV_MM_DELAY: + memcpy(tab32, value, TE_LINK_SUBTLV_MM_DELAY_SIZE); + attr.extended.min_delay = ntohl(tab32[0]); + attr.extended.max_delay = ntohl(tab32[1]); + SET_FLAG(attr.flags, LS_ATTR_MIN_MAX_DELAY); + break; + case TE_LINK_SUBTLV_DELAY_VAR: + memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.extended.jitter = ntohl(val32); + SET_FLAG(attr.flags, LS_ATTR_JITTER); + break; + case TE_LINK_SUBTLV_PKT_LOSS: + memcpy(&val32, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.extended.pkt_loss = ntohl(val32); + SET_FLAG(attr.flags, LS_ATTR_PACKET_LOSS); + break; + case TE_LINK_SUBTLV_RES_BW: + memcpy(&valf, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.extended.rsv_bw = ntohf(valf); + SET_FLAG(attr.flags, LS_ATTR_RSV_BW); + break; + case TE_LINK_SUBTLV_AVA_BW: + memcpy(&valf, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.extended.ava_bw = ntohf(valf); + SET_FLAG(attr.flags, LS_ATTR_AVA_BW); + break; + case TE_LINK_SUBTLV_USE_BW: + memcpy(&valf, value, TE_LINK_SUBTLV_DEF_SIZE); + attr.extended.used_bw = ntohf(valf); + SET_FLAG(attr.flags, LS_ATTR_USE_BW); + break; + default: + break; + } + sum += TLV_SIZE(tlvh); + } + + /* Get corresponding Edge from Link State Data Base */ + edge = get_edge(ted, attr.adv, attr.standard.local); + old = edge->attributes; + + ote_debug(" |- Process Traffic Engineering LSA %pI4 for Edge %pI4", + &lsa->data->id, &attr.standard.local); + + /* Update standard fields */ + len = sizeof(struct ls_standard); + if ((attr.flags & 0x0FFFF) == (old->flags & 0x0FFFF)) { + if (memcmp(&attr.standard, &old->standard, len) != 0) { + memcpy(&old->standard, &attr.standard, len); + if (edge->status != NEW) + edge->status = UPDATE; + } + } else { + memcpy(&old->standard, &attr.standard, len); + old->flags |= attr.flags & 0x0FFFF; + if (edge->status != NEW) + edge->status = UPDATE; + } + /* Update extended fields */ + len = sizeof(struct ls_extended); + if ((attr.flags & 0x0FF0000) == (old->flags & 0x0FF0000)) { + if (memcmp(&attr.extended, &old->extended, len) != 0) { + memcpy(&old->extended, &attr.extended, len); + if (edge->status != NEW) + edge->status = UPDATE; + } + } else { + memcpy(&old->extended, &attr.extended, len); + old->flags |= attr.flags & 0x0FF0000; + if (edge->status != NEW) + edge->status = UPDATE; + } + + /* If LSA is an Opaque Inter-AS, Add Node and Subnet */ + lsa_id = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)); + if (lsa_id == OPAQUE_TYPE_INTER_AS_LSA) + ospf_te_update_remote_asbr(ted, edge); + + /* Update remote Link if remote IP addr is known */ + if (CHECK_FLAG(old->flags, LS_ATTR_NEIGH_ADDR)) { + struct ls_edge *dst; + + dst = ls_find_edge_by_destination(ted, old); + /* Attach remote link if not set */ + if (dst && edge->source && dst->destination == NULL) { + vertex = edge->source; + if (vertex->incoming_edges) + listnode_add_sort_nodup(vertex->incoming_edges, + dst); + dst->destination = vertex; + } + /* and destination vertex to this edge */ + if (dst && dst->source && edge->destination == NULL) { + vertex = dst->source; + if (vertex->incoming_edges) + listnode_add_sort_nodup(vertex->incoming_edges, + edge); + edge->destination = vertex; + } + } + + /* Export Link State Edge if needed */ + if (edge->status == NEW || edge->status == UPDATE) { + ote_debug(" |- %s TE info. for Edge %pI4", + edge->status == NEW ? "Add" : "Update", + &edge->attributes->standard.local); + + ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); + edge->status = SYNC; + } + + return 0; +} + +/** + * Delete Link State Attributes information that correspond to the Opaque + * Traffic Engineering LSA (Type 1) TLVs. Note that the Edge is not removed. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_delete_te(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_edge *edge; + struct ls_attributes *attr; + struct tlv_header *tlvh; + struct in_addr addr; + struct ls_edge_key key = {.family = AF_UNSPEC}; + uint16_t len, sum; + uint8_t lsa_id; + + /* Initialize TLV browsing */ + tlvh = TLV_HDR_TOP(lsa->data); + /* Skip Router TE ID if present */ + if (ntohs(tlvh->type) == TE_TLV_ROUTER_ADDR) + tlvh = TLV_HDR_NEXT(tlvh); + len = TLV_BODY_SIZE(tlvh); + sum = sizeof(struct tlv_header); + + /* Browse sub-TLV to find Link ID */ + for (tlvh = TLV_DATA(tlvh); sum < len; tlvh = TLV_HDR_NEXT(tlvh)) { + if (ntohs(tlvh->type) == TE_LINK_SUBTLV_LCLIF_IPADDR) { + memcpy(&addr, TLV_DATA(tlvh), TE_LINK_SUBTLV_DEF_SIZE); + key.family = AF_INET; + IPV4_ADDR_COPY(&key.k.addr, &addr); + break; + } + sum += TLV_SIZE(tlvh); + } + if (key.family == AF_UNSPEC) + return 0; + + /* Search Edge that corresponds to the Link ID */ + edge = ls_find_edge_by_key(ted, key); + if (!edge || !edge->attributes) + return 0; + attr = edge->attributes; + + /* First, remove Remote ASBR and associated Edge & Subnet if any */ + lsa_id = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)); + if (lsa_id == OPAQUE_TYPE_INTER_AS_LSA) { + ote_debug(" |- Delete remote ASBR, Edge and Subnet"); + + if (edge->destination) { + edge->destination->status = DELETE; + ospf_te_export(LS_MSG_TYPE_NODE, edge->destination); + ls_vertex_del_all(ted, edge->destination); + } + + ospf_te_delete_subnet(ted, attr->standard.local); + + edge->status = DELETE; + ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); + ls_edge_del_all(ted, edge); + + return 0; + } + + ote_debug(" |- Delete TE info. for Edge %pI4", + &edge->attributes->standard.local); + + /* Remove Link State Attributes TE information */ + memset(&attr->standard, 0, sizeof(struct ls_standard)); + attr->flags &= 0x0FFFF; + memset(&attr->extended, 0, sizeof(struct ls_extended)); + attr->flags &= 0x0FF0000; + ls_attributes_srlg_del(attr); + + /* Export Edge that has been updated */ + if (CHECK_FLAG(attr->flags, LS_ATTR_ADJ_SID) + || CHECK_FLAG(attr->flags, LS_ATTR_BCK_ADJ_SID)) { + edge->status = UPDATE; + ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); + edge->status = SYNC; + } else { + /* Remove completely the Edge if Segment Routing is not set */ + ospf_te_delete_subnet(ted, attr->standard.local); + edge->status = DELETE; + ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); + ls_edge_del_all(ted, edge); + } + + return 0; +} + +/** + * Parse Opaque Router Information LSA (Type 4) TLVs and update the + * corresponding Link State Vertex with these information (Segment Routing). + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_parse_ri(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_vertex *vertex; + struct ls_node *node; + struct lsa_header *lsah = lsa->data; + struct tlv_header *tlvh; + uint16_t len = 0, sum = 0; + + /* Get vertex / Node from LSA Advertised Router ID */ + vertex = get_vertex(ted, lsa); + node = vertex->node; + + ote_debug(" |- Process Router Information LSA %pI4 for Vertex %pI4", + &lsa->data->id, &node->router_id); + + /* Initialize TLV browsing */ + len = lsa->size - OSPF_LSA_HEADER_SIZE; + for (tlvh = TLV_HDR_TOP(lsah); sum < len && tlvh; + tlvh = TLV_HDR_NEXT(tlvh)) { + struct ri_sr_tlv_sr_algorithm *algo; + struct ri_sr_tlv_sid_label_range *range; + struct ri_sr_tlv_node_msd *msd; + uint32_t size, lower; + + switch (ntohs(tlvh->type)) { + case RI_SR_TLV_SR_ALGORITHM: + algo = (struct ri_sr_tlv_sr_algorithm *)tlvh; + + for (int i = 0; i < ntohs(algo->header.length); i++) { + if (CHECK_FLAG(node->flags, LS_NODE_SR) + && (node->algo[i] == algo->value[i])) + continue; + + node->algo[i] = algo->value[i]; + SET_FLAG(node->flags, LS_NODE_SR); + if (vertex->status != NEW) + vertex->status = UPDATE; + } + + /* Reset other Algorithms */ + for (int i = ntohs(algo->header.length); i < 2; i++) { + if (vertex->status != NEW + && node->algo[i] != SR_ALGORITHM_UNSET) + vertex->status = UPDATE; + node->algo[i] = SR_ALGORITHM_UNSET; + } + + break; + + case RI_SR_TLV_SRGB_LABEL_RANGE: + range = (struct ri_sr_tlv_sid_label_range *)tlvh; + size = GET_RANGE_SIZE(ntohl(range->size)); + lower = GET_LABEL(ntohl(range->lower.value)); + if ((CHECK_FLAG(node->flags, LS_NODE_SR)) + && ((node->srgb.range_size == size) + && (node->srgb.lower_bound == lower))) + break; + + node->srgb.range_size = size; + node->srgb.lower_bound = lower; + SET_FLAG(node->flags, LS_NODE_SR); + if (vertex->status != NEW) + vertex->status = UPDATE; + + break; + + case RI_SR_TLV_SRLB_LABEL_RANGE: + range = (struct ri_sr_tlv_sid_label_range *)tlvh; + size = GET_RANGE_SIZE(ntohl(range->size)); + lower = GET_LABEL(ntohl(range->lower.value)); + if ((CHECK_FLAG(node->flags, LS_NODE_SRLB)) + && ((node->srlb.range_size == size) + && (node->srlb.lower_bound == lower))) + break; + + node->srlb.range_size = size; + node->srlb.lower_bound = lower; + SET_FLAG(node->flags, LS_NODE_SRLB); + if (vertex->status != NEW) + vertex->status = UPDATE; + + break; + + case RI_SR_TLV_NODE_MSD: + msd = (struct ri_sr_tlv_node_msd *)tlvh; + if ((CHECK_FLAG(node->flags, LS_NODE_MSD)) + && (node->msd == msd->value)) + break; + + node->msd = msd->value; + SET_FLAG(node->flags, LS_NODE_MSD); + if (vertex->status != NEW) + vertex->status = UPDATE; + + break; + + default: + break; + } + sum += TLV_SIZE(tlvh); + } + + /* Vertex has been created or updated: export it */ + if (vertex->status == NEW || vertex->status == UPDATE) { + ote_debug(" |- %s SR info - SRGB[%d/%d] for Vertex %pI4", + vertex->status == NEW ? "Add" : "Update", + vertex->node->srgb.lower_bound, + vertex->node->srgb.range_size, + &vertex->node->router_id); + + ospf_te_export(LS_MSG_TYPE_NODE, vertex); + vertex->status = SYNC; + } + + return 0; +} + +/** + * Delete Link State Node information (Segment Routing) that correspond to the + * Opaque Router Information LSA (Type 4) TLVs. Note that the Vertex is not + * removed. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_delete_ri(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_node_id lnid; + struct ls_vertex *vertex; + struct ls_node *node; + + /* Search if a Link State Vertex already exist */ + lnid.origin = OSPFv2; + lnid.id.ip.addr = lsa->data->adv_router; + lnid.id.ip.area_id = lsa->area->area_id; + vertex = ls_find_vertex_by_id(ted, lnid); + if (!vertex) + return -1; + + /* Remove Segment Routing Information if any */ + node = vertex->node; + UNSET_FLAG(node->flags, LS_NODE_SR); + memset(&node->srgb, 0, sizeof(struct ls_srgb)); + node->algo[0] = SR_ALGORITHM_UNSET; + node->algo[1] = SR_ALGORITHM_UNSET; + UNSET_FLAG(node->flags, LS_NODE_SRLB); + memset(&node->srlb, 0, sizeof(struct ls_srlb)); + UNSET_FLAG(node->flags, LS_NODE_MSD); + node->msd = 0; + vertex->status = UPDATE; + + ote_debug(" |- Delete SR info. for Vertex %pI4", + &vertex->node->router_id); + + /* Vertex has been updated: export it */ + ospf_te_export(LS_MSG_TYPE_NODE, vertex); + vertex->status = SYNC; + + return 0; +} + +/** + * Parse Opaque Extended Prefix LSA (Type 7) TLVs and update the corresponding + * Link State Subnet with these information (Segment Routing ID). + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_parse_ext_pref(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_node_id lnid; + struct ls_subnet *subnet; + struct ls_prefix *ls_pref; + struct prefix pref; + struct ext_tlv_prefix *ext; + struct ext_subtlv_prefix_sid *pref_sid; + uint32_t label; + + /* Get corresponding Subnet from Link State Data Base */ + ext = (struct ext_tlv_prefix *)TLV_HDR_TOP(lsa->data); + pref.family = AF_INET; + pref.prefixlen = ext->pref_length; + pref.u.prefix4 = ext->address; + subnet = ls_find_subnet(ted, &pref); + + /* Create new Link State Prefix if not found */ + if (!subnet) { + lnid.origin = OSPFv2; + lnid.id.ip.addr = lsa->data->adv_router; + lnid.id.ip.area_id = lsa->area->area_id; + ls_pref = ls_prefix_new(lnid, &pref); + /* and add it to the TED */ + subnet = ls_subnet_add(ted, ls_pref); + } + + ote_debug(" |- Process Extended Prefix LSA %pI4 for subnet %pFX", + &lsa->data->id, &pref); + + /* Initialize TLV browsing */ + ls_pref = subnet->ls_pref; + pref_sid = (struct ext_subtlv_prefix_sid *)((char *)(ext) + TLV_HDR_SIZE + + EXT_TLV_PREFIX_SIZE); + label = CHECK_FLAG(pref_sid->flags, EXT_SUBTLV_PREFIX_SID_VFLG) + ? GET_LABEL(ntohl(pref_sid->value)) + : ntohl(pref_sid->value); + + /* Check if it is a simple refresh */ + if (CHECK_FLAG(ls_pref->flags, LS_PREF_SR) + && ls_pref->sr.algo == pref_sid->algorithm + && ls_pref->sr.sid_flag == pref_sid->flags + && ls_pref->sr.sid == label) + return 0; + + /* Fulfill SR information */ + ls_pref->sr.algo = pref_sid->algorithm; + ls_pref->sr.sid_flag = pref_sid->flags; + ls_pref->sr.sid = label; + SET_FLAG(ls_pref->flags, LS_PREF_SR); + if (subnet->status != NEW) + subnet->status = UPDATE; + + /* Export Subnet if needed */ + if (subnet->status == NEW || subnet->status == UPDATE) { + ote_debug(" |- %s SID %d to subnet %pFX", + subnet->status == NEW ? "Add" : "Update", + ls_pref->sr.sid, &ls_pref->pref); + + ospf_te_export(LS_MSG_TYPE_PREFIX, subnet); + subnet->status = SYNC; + } + + return 0; +} + +/** + * Delete Link State Subnet information (Segment Routing ID) that correspond to + * the Opaque Extended Prefix LSA (Type 7) TLVs. Note that the Subnet is not + * removed. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_delete_ext_pref(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_subnet *subnet; + struct ls_prefix *ls_pref; + struct prefix pref; + struct ext_tlv_prefix *ext; + + /* Get corresponding Subnet from Link State Data Base */ + ext = (struct ext_tlv_prefix *)TLV_HDR_TOP(lsa->data); + pref.family = AF_INET; + pref.prefixlen = ext->pref_length; + pref.u.prefix4 = ext->address; + subnet = ls_find_subnet(ted, &pref); + + /* Check if there is a corresponding subnet */ + if (!subnet) + return -1; + + ote_debug(" |- Delete SID %d to subnet %pFX", subnet->ls_pref->sr.sid, + &subnet->ls_pref->pref); + + /* Remove Segment Routing information */ + ls_pref = subnet->ls_pref; + UNSET_FLAG(ls_pref->flags, LS_PREF_SR); + memset(&ls_pref->sr, 0, sizeof(struct ls_sid)); + subnet->status = UPDATE; + + /* Subnet has been updated: export it */ + ospf_te_export(LS_MSG_TYPE_PREFIX, subnet); + subnet->status = SYNC; + + return 0; +} + +/** + * Parse Opaque Extended Link LSA (Type 8) TLVs and update the corresponding + * Link State Edge with these information (Segment Routing Adjacency). + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_parse_ext_link(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_node_id lnid; + struct tlv_header *tlvh; + struct ext_tlv_link *ext; + struct ls_edge *edge; + struct ls_attributes *atr; + uint16_t len = 0, sum = 0, i; + uint32_t label; + + /* Get corresponding Edge from Link State Data Base */ + lnid.origin = OSPFv2; + lnid.id.ip.addr = lsa->data->adv_router; + lnid.id.ip.area_id = lsa->area->area_id; + ext = (struct ext_tlv_link *)TLV_HDR_TOP(lsa->data); + edge = get_edge(ted, lnid, ext->link_data); + atr = edge->attributes; + + ote_debug(" |- Process Extended Link LSA %pI4 for edge %pI4", + &lsa->data->id, &edge->attributes->standard.local); + + /* Initialize TLV browsing */ + len = TLV_BODY_SIZE(&ext->header) - EXT_TLV_LINK_SIZE; + tlvh = (struct tlv_header *)((char *)(ext) + TLV_HDR_SIZE + + EXT_TLV_LINK_SIZE); + for (; sum < len; tlvh = TLV_HDR_NEXT(tlvh)) { + struct ext_subtlv_adj_sid *adj; + struct ext_subtlv_lan_adj_sid *ladj; + struct ext_subtlv_rmt_itf_addr *rmt; + + switch (ntohs(tlvh->type)) { + case EXT_SUBTLV_ADJ_SID: + adj = (struct ext_subtlv_adj_sid *)tlvh; + label = CHECK_FLAG(adj->flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG) + ? GET_LABEL(ntohl(adj->value)) + : ntohl(adj->value); + i = CHECK_FLAG(adj->flags, + EXT_SUBTLV_LINK_ADJ_SID_BFLG) ? 1 : 0; + if (((i && CHECK_FLAG(atr->flags, LS_ATTR_BCK_ADJ_SID)) + || (!i && CHECK_FLAG(atr->flags, LS_ATTR_ADJ_SID))) + && atr->adj_sid[i].flags == adj->flags + && atr->adj_sid[i].sid == label + && atr->adj_sid[i].weight == adj->weight) + break; + + atr->adj_sid[i].flags = adj->flags; + atr->adj_sid[i].sid = label; + atr->adj_sid[i].weight = adj->weight; + if (i == 0) + SET_FLAG(atr->flags, LS_ATTR_ADJ_SID); + else + SET_FLAG(atr->flags, LS_ATTR_BCK_ADJ_SID); + if (edge->status != NEW) + edge->status = UPDATE; + + break; + case EXT_SUBTLV_LAN_ADJ_SID: + ladj = (struct ext_subtlv_lan_adj_sid *)tlvh; + label = CHECK_FLAG(ladj->flags, + EXT_SUBTLV_LINK_ADJ_SID_VFLG) + ? GET_LABEL(ntohl(ladj->value)) + : ntohl(ladj->value); + i = CHECK_FLAG(ladj->flags, + EXT_SUBTLV_LINK_ADJ_SID_BFLG) ? 1 : 0; + if (((i && CHECK_FLAG(atr->flags, LS_ATTR_BCK_ADJ_SID)) + || (!i && CHECK_FLAG(atr->flags, LS_ATTR_ADJ_SID))) + && atr->adj_sid[i].flags == ladj->flags + && atr->adj_sid[i].sid == label + && atr->adj_sid[i].weight == ladj->weight + && IPV4_ADDR_SAME(&atr->adj_sid[1].neighbor.addr, + &ladj->neighbor_id)) + break; + + atr->adj_sid[i].flags = ladj->flags; + atr->adj_sid[i].sid = label; + atr->adj_sid[i].weight = ladj->weight; + atr->adj_sid[i].neighbor.addr = ladj->neighbor_id; + if (i == 0) + SET_FLAG(atr->flags, LS_ATTR_ADJ_SID); + else + SET_FLAG(atr->flags, LS_ATTR_BCK_ADJ_SID); + if (edge->status != NEW) + edge->status = UPDATE; + + break; + case EXT_SUBTLV_RMT_ITF_ADDR: + rmt = (struct ext_subtlv_rmt_itf_addr *)tlvh; + if (CHECK_FLAG(atr->flags, LS_ATTR_NEIGH_ADDR) + && IPV4_ADDR_SAME(&atr->standard.remote, + &rmt->value)) + break; + + atr->standard.remote = rmt->value; + SET_FLAG(atr->flags, LS_ATTR_NEIGH_ADDR); + if (edge->status != NEW) + edge->status = UPDATE; + + break; + default: + break; + } + sum += TLV_SIZE(tlvh); + } + + /* Export Link State Edge if needed */ + if (edge->status == NEW || edge->status == UPDATE) { + ote_debug(" |- %s Adj-SID %d & %d to edge %pI4", + edge->status == NEW ? "Add" : "Update", + edge->attributes->adj_sid[0].sid, + edge->attributes->adj_sid[1].sid, + &edge->attributes->standard.local); + + ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); + edge->status = SYNC; + } + + return 0; +} + +/** + * Delete Link State Edge information (Segment Routing Adjacency) that + * correspond to the Opaque Extended Link LSA (Type 8) TLVs. Note that the Edge + * is not removed. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_delete_ext_link(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + struct ls_edge *edge; + struct ls_attributes *atr; + struct ext_tlv_link *ext; + struct ls_edge_key key; + + /* Search for corresponding Edge from Link State Data Base */ + ext = (struct ext_tlv_link *)TLV_HDR_TOP(lsa->data); + key.family = AF_INET; + IPV4_ADDR_COPY(&key.k.addr, &ext->link_data); + edge = ls_find_edge_by_key(ted, key); + + /* Check if there is a corresponding Edge */ + if (!edge) + return -1; + + ote_debug(" |- Delete Adj-SID %d to edge %pI4", + edge->attributes->adj_sid[0].sid, + &edge->attributes->standard.local); + + /* Remove Segment Routing information */ + atr = edge->attributes; + UNSET_FLAG(atr->flags, LS_ATTR_ADJ_SID); + UNSET_FLAG(atr->flags, LS_ATTR_BCK_ADJ_SID); + memset(atr->adj_sid, 0, 2 * sizeof(struct ls_sid)); + edge->status = UPDATE; + + /* Edge has been updated: export it */ + ospf_te_export(LS_MSG_TYPE_ATTRIBUTES, edge); + edge->status = SYNC; + + return 0; +} + +/** + * Parse Opaque LSA Type and call corresponding parser. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_parse_opaque_lsa(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + uint8_t key = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)); + int rc = -1; + + ote_debug("MPLS-TE (%s): Parse Opaque LSA[%pI4] from Router[%pI4]", + __func__, &lsa->data->id, &lsa->data->adv_router); + + switch (key) { + case OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA: + case OPAQUE_TYPE_INTER_AS_LSA: + rc = ospf_te_parse_te(ted, lsa); + break; + case OPAQUE_TYPE_ROUTER_INFORMATION_LSA: + rc = ospf_te_parse_ri(ted, lsa); + break; + case OPAQUE_TYPE_EXTENDED_PREFIX_LSA: + rc = ospf_te_parse_ext_pref(ted, lsa); + break; + case OPAQUE_TYPE_EXTENDED_LINK_LSA: + rc = ospf_te_parse_ext_link(ted, lsa); + break; + default: + break; + } + + return rc; +} + +/** + * Parse Opaque LSA Type and call corresponding deletion function. + * + * @param ted Link State Traffic Engineering Database + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_te_delete_opaque_lsa(struct ls_ted *ted, struct ospf_lsa *lsa) +{ + uint8_t key = GET_OPAQUE_TYPE(ntohl(lsa->data->id.s_addr)); + int rc = -1; + + ote_debug("MPLS-TE (%s): Parse Opaque LSA[%pI4] from Router[%pI4]", + __func__, &lsa->data->id, &lsa->data->adv_router); + + switch (key) { + case OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA: + case OPAQUE_TYPE_INTER_AS_LSA: + rc = ospf_te_delete_te(ted, lsa); + break; + case OPAQUE_TYPE_ROUTER_INFORMATION_LSA: + rc = ospf_te_delete_ri(ted, lsa); + break; + case OPAQUE_TYPE_EXTENDED_PREFIX_LSA: + rc = ospf_te_delete_ext_pref(ted, lsa); + break; + case OPAQUE_TYPE_EXTENDED_LINK_LSA: + rc = ospf_te_delete_ext_link(ted, lsa); + break; + default: + break; + } + + return rc; +} + +/** + * Update Traffic Engineering Database Elements that correspond to the received + * OSPF LSA. If LSA age is equal to MAX_AGE, call deletion function instead. + * + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_mpls_te_lsa_update(struct ospf_lsa *lsa) +{ + + uint8_t rc; + + /* Check that MPLS-TE is active */ + if (!OspfMplsTE.enabled || !OspfMplsTE.ted) + return 0; + + /* Sanity Check */ + if (lsa == NULL) { + flog_warn(EC_OSPF_LSA_NULL, "TE (%s): Abort! LSA is NULL", + __func__); + return -1; + } + + /* If LSA is MAX_AGE, remove corresponding Link State element */ + if (IS_LSA_MAXAGE(lsa)) { + switch (lsa->data->type) { + case OSPF_ROUTER_LSA: + rc = ospf_te_delete_router_lsa(OspfMplsTE.ted, lsa); + break; + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + rc = ospf_te_delete_opaque_lsa(OspfMplsTE.ted, lsa); + break; + default: + rc = 0; + break; + } + } else { + /* Parse LSA to Update corresponding Link State element */ + switch (lsa->data->type) { + case OSPF_ROUTER_LSA: + rc = ospf_te_parse_router_lsa(OspfMplsTE.ted, lsa); + break; + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + rc = ospf_te_parse_opaque_lsa(OspfMplsTE.ted, lsa); + break; + default: + rc = 0; + break; + } + } + + return rc; +} + +/** + * Delete Traffic Engineering Database element from OSPF LSA. This function + * process only self LSA (i.e. advertised by the router) which reach MAX_AGE + * as LSA deleted by neighbor routers are Flushed (i.e. advertised with + * age == MAX_AGE) and processed by ospf_mpls_te_lsa_update() function. + * + * @param lsa OSPF Link State Advertisement + * + * @return 0 if success, -1 otherwise + */ +static int ospf_mpls_te_lsa_delete(struct ospf_lsa *lsa) +{ + + uint8_t rc; + + /* Check that MPLS-TE is active */ + if (!OspfMplsTE.enabled || !OspfMplsTE.ted) + return 0; + + /* Sanity Check */ + if (lsa == NULL) { + flog_warn(EC_OSPF_LSA_NULL, "TE (%s): Abort! LSA is NULL", + __func__); + return -1; + } + + /* + * Process only self LSAs that reach MAX_AGE. Indeed, when the router + * need to update or refresh an LSA, it first removes the old LSA from + * the LSDB and then insert the new one. Thus, to avoid removing + * corresponding Link State element and loosing some parameters + * instead of just updating it, only self LSAs that reach MAX_AGE are + * processed here. Other LSAs are processed by ospf_mpls_te_lsa_update() + * and eventually removed when LSA age is MAX_AGE i.e. LSA is flushed + * by the originator. + */ + if (!IS_LSA_SELF(lsa) || !IS_LSA_MAXAGE(lsa)) + return 0; + + /* Parse Link State information */ + switch (lsa->data->type) { + case OSPF_ROUTER_LSA: + rc = ospf_te_delete_router_lsa(OspfMplsTE.ted, lsa); + break; + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + rc = ospf_te_delete_opaque_lsa(OspfMplsTE.ted, lsa); + break; + default: + rc = 0; + break; + } + + return rc; +} + +/** + * Send the whole Link State Traffic Engineering Database to the consumer that + * request it through a ZAPI Link State Synchronous Opaque Message. + * + * @param info ZAPI Opaque message + * + * @return 0 if success, -1 otherwise + */ +int ospf_te_sync_ted(struct zapi_opaque_reg_info dst) +{ + int rc = -1; + + /* Check that MPLS-TE and TE distribution are enabled */ + if (!OspfMplsTE.enabled || !OspfMplsTE.export) + return rc; + + rc = ls_sync_ted(OspfMplsTE.ted, zclient, &dst); + + return rc; +} + +/** + * Initialize Traffic Engineering Database from the various OSPF Link State + * Database (LSDB). + * + * @param ted Link State Traffice Engineering Database + * @param ospf OSPF main structure + */ +static void ospf_te_init_ted(struct ls_ted *ted, struct ospf *ospf) +{ + struct listnode *node, *nnode; + struct route_node *rn; + struct ospf_area *area; + struct ospf_lsa *lsa; + + /* Iterate over all areas. */ + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + if (!area->lsdb) + continue; + + /* Parse all Router LSAs from the area LSDB */ + LSDB_LOOP (ROUTER_LSDB(area), rn, lsa) + ospf_te_parse_router_lsa(ted, lsa); + + /* Parse all Opaque LSAs from the area LSDB */ + LSDB_LOOP (OPAQUE_AREA_LSDB(area), rn, lsa) + ospf_te_parse_opaque_lsa(ted, lsa); + } + + /* Parse AS-external opaque LSAs from OSPF LSDB */ + if (ospf->lsdb) { + LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa) + ospf_te_parse_opaque_lsa(ted, lsa); + } + +} + +/*------------------------------------------------------------------------* + * Following are vty session control functions. + *------------------------------------------------------------------------*/ +#define check_tlv_size(size, msg) \ + do { \ + if (ntohs(tlvh->length) > size) { \ + if (vty != NULL) \ + vty_out(vty, " Wrong %s TLV size: %d(%d)\n", \ + msg, ntohs(tlvh->length), size); \ + else \ + zlog_debug(" Wrong %s TLV size: %d(%d)", \ + msg, ntohs(tlvh->length), size); \ + return size + TLV_HDR_SIZE; \ + } \ + } while (0) + +static uint16_t show_vty_router_addr(struct vty *vty, struct tlv_header *tlvh) +{ + struct te_tlv_router_addr *top = (struct te_tlv_router_addr *)tlvh; + + check_tlv_size(TE_LINK_SUBTLV_DEF_SIZE, "Router Address"); + + if (vty != NULL) + vty_out(vty, " Router-Address: %pI4\n", &top->value); + else + zlog_debug(" Router-Address: %pI4", &top->value); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_header(struct vty *vty, struct tlv_header *tlvh, + size_t buf_size) +{ + struct te_tlv_link *top = (struct te_tlv_link *)tlvh; + + if (TLV_SIZE(tlvh) > buf_size) { + if (vty != NULL) + vty_out(vty, + " TLV size %d exceeds buffer size. Abort!", + TLV_SIZE(tlvh)); + else + zlog_debug( + " TLV size %d exceeds buffer size. Abort!", + TLV_SIZE(tlvh)); + return buf_size; + } + + if (vty != NULL) + vty_out(vty, " Link: %u octets of data\n", + ntohs(top->header.length)); + else + zlog_debug(" Link: %u octets of data", + ntohs(top->header.length)); + + return TLV_HDR_SIZE; /* Here is special, not "TLV_SIZE". */ +} + +static uint16_t show_vty_link_subtlv_link_type(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_link_type *top; + const char *cp = "Unknown"; + + check_tlv_size(TE_LINK_SUBTLV_TYPE_SIZE, "Link Type"); + + top = (struct te_link_subtlv_link_type *)tlvh; + switch (top->link_type.value) { + case LINK_TYPE_SUBTLV_VALUE_PTP: + cp = "Point-to-point"; + break; + case LINK_TYPE_SUBTLV_VALUE_MA: + cp = "Multiaccess"; + break; + default: + break; + } + + if (vty != NULL) + vty_out(vty, " Link-Type: %s (%u)\n", cp, + top->link_type.value); + else + zlog_debug(" Link-Type: %s (%u)", cp, top->link_type.value); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_link_id(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_link_id *top; + + check_tlv_size(TE_LINK_SUBTLV_DEF_SIZE, "Link ID"); + + top = (struct te_link_subtlv_link_id *)tlvh; + if (vty != NULL) + vty_out(vty, " Link-ID: %pI4\n", &top->value); + else + zlog_debug(" Link-ID: %pI4", &top->value); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_lclif_ipaddr(struct vty *vty, + struct tlv_header *tlvh, + size_t buf_size) +{ + struct te_link_subtlv_lclif_ipaddr *top; + int i, n; + + if (TLV_SIZE(tlvh) > buf_size) { + if (vty != NULL) + vty_out(vty, + " TLV size %d exceeds buffer size. Abort!", + TLV_SIZE(tlvh)); + else + zlog_debug( + " TLV size %d exceeds buffer size. Abort!", + TLV_SIZE(tlvh)); + return buf_size; + } + + top = (struct te_link_subtlv_lclif_ipaddr *)tlvh; + n = ntohs(tlvh->length) / sizeof(top->value[0]); + + if (vty != NULL) + vty_out(vty, " Local Interface IP Address(es): %d\n", n); + else + zlog_debug(" Local Interface IP Address(es): %d", n); + + for (i = 0; i < n; i++) { + if (vty != NULL) + vty_out(vty, " #%d: %pI4\n", i, &top->value[i]); + else + zlog_debug(" #%d: %pI4", i, &top->value[i]); + } + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_rmtif_ipaddr(struct vty *vty, + struct tlv_header *tlvh, + size_t buf_size) +{ + struct te_link_subtlv_rmtif_ipaddr *top; + int i, n; + + if (TLV_SIZE(tlvh) > buf_size) { + if (vty != NULL) + vty_out(vty, + " TLV size %d exceeds buffer size. Abort!", + TLV_SIZE(tlvh)); + else + zlog_debug( + " TLV size %d exceeds buffer size. Abort!", + TLV_SIZE(tlvh)); + return buf_size; + } + + top = (struct te_link_subtlv_rmtif_ipaddr *)tlvh; + n = ntohs(tlvh->length) / sizeof(top->value[0]); + if (vty != NULL) + vty_out(vty, " Remote Interface IP Address(es): %d\n", n); + else + zlog_debug(" Remote Interface IP Address(es): %d", n); + + for (i = 0; i < n; i++) { + if (vty != NULL) + vty_out(vty, " #%d: %pI4\n", i, &top->value[i]); + else + zlog_debug(" #%d: %pI4", i, &top->value[i]); + } + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_te_metric(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_te_metric *top; + + check_tlv_size(TE_LINK_SUBTLV_DEF_SIZE, "TE Metric"); + + top = (struct te_link_subtlv_te_metric *)tlvh; + if (vty != NULL) + vty_out(vty, " Traffic Engineering Metric: %u\n", + (uint32_t)ntohl(top->value)); + else + zlog_debug(" Traffic Engineering Metric: %u", + (uint32_t)ntohl(top->value)); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_max_bw(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_max_bw *top; + float fval; + + check_tlv_size(TE_LINK_SUBTLV_DEF_SIZE, "Maximum Bandwidth"); + + top = (struct te_link_subtlv_max_bw *)tlvh; + fval = ntohf(top->value); + + if (vty != NULL) + vty_out(vty, " Maximum Bandwidth: %g (Bytes/sec)\n", fval); + else + zlog_debug(" Maximum Bandwidth: %g (Bytes/sec)", fval); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_max_rsv_bw(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_max_rsv_bw *top; + float fval; + + check_tlv_size(TE_LINK_SUBTLV_DEF_SIZE, "Maximum Reservable Bandwidth"); + + top = (struct te_link_subtlv_max_rsv_bw *)tlvh; + fval = ntohf(top->value); + + if (vty != NULL) + vty_out(vty, " Maximum Reservable Bandwidth: %g (Bytes/sec)\n", + fval); + else + zlog_debug(" Maximum Reservable Bandwidth: %g (Bytes/sec)", + fval); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_unrsv_bw(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_unrsv_bw *top; + float fval1, fval2; + int i; + + check_tlv_size(TE_LINK_SUBTLV_UNRSV_SIZE, "Unreserved Bandwidth"); + + top = (struct te_link_subtlv_unrsv_bw *)tlvh; + if (vty != NULL) + vty_out(vty, + " Unreserved Bandwidth per Class Type in Byte/s:\n"); + else + zlog_debug( + " Unreserved Bandwidth per Class Type in Byte/s:"); + for (i = 0; i < MAX_CLASS_TYPE; i += 2) { + fval1 = ntohf(top->value[i]); + fval2 = ntohf(top->value[i + 1]); + + if (vty != NULL) + vty_out(vty, + " [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n", + i, fval1, i + 1, fval2); + else + zlog_debug( + " [%d]: %g (Bytes/sec), [%d]: %g (Bytes/sec)", + i, fval1, i + 1, fval2); + } + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_rsc_clsclr(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_rsc_clsclr *top; + + check_tlv_size(TE_LINK_SUBTLV_DEF_SIZE, "Resource class/color"); + + top = (struct te_link_subtlv_rsc_clsclr *)tlvh; + if (vty != NULL) + vty_out(vty, " Resource class/color: 0x%x\n", + (uint32_t)ntohl(top->value)); + else + zlog_debug(" Resource Class/Color: 0x%x", + (uint32_t)ntohl(top->value)); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_lrrid(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_lrrid *top; + + check_tlv_size(TE_LINK_SUBTLV_LRRID_SIZE, "Local/Remote Router ID"); + + top = (struct te_link_subtlv_lrrid *)tlvh; + + if (vty != NULL) { + vty_out(vty, " Local TE Router ID: %pI4\n", + &top->local); + vty_out(vty, " Remote TE Router ID: %pI4\n", + &top->remote); + } else { + zlog_debug(" Local TE Router ID: %pI4", + &top->local); + zlog_debug(" Remote TE Router ID: %pI4", + &top->remote); + } + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_llri(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_llri *top; + + check_tlv_size(TE_LINK_SUBTLV_LLRI_SIZE, "Link Local/Remote ID"); + + top = (struct te_link_subtlv_llri *)tlvh; + + if (vty != NULL) { + vty_out(vty, " Link Local ID: %d\n", + (uint32_t)ntohl(top->local)); + vty_out(vty, " Link Remote ID: %d\n", + (uint32_t)ntohl(top->remote)); + } else { + zlog_debug(" Link Local ID: %d", + (uint32_t)ntohl(top->local)); + zlog_debug(" Link Remote ID: %d", + (uint32_t)ntohl(top->remote)); + } + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_rip(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_rip *top; + + check_tlv_size(TE_LINK_SUBTLV_DEF_SIZE, "Remote ASBR Address"); + + top = (struct te_link_subtlv_rip *)tlvh; + + if (vty != NULL) + vty_out(vty, " Inter-AS TE Remote ASBR IP address: %pI4\n", + &top->value); + else + zlog_debug(" Inter-AS TE Remote ASBR IP address: %pI4", + &top->value); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_ras(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_ras *top; + + check_tlv_size(TE_LINK_SUBTLV_DEF_SIZE, "Remote AS number"); + + top = (struct te_link_subtlv_ras *)tlvh; + + if (vty != NULL) + vty_out(vty, " Inter-AS TE Remote AS number: %u\n", + ntohl(top->value)); + else + zlog_debug(" Inter-AS TE Remote AS number: %u", + ntohl(top->value)); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_av_delay(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_av_delay *top; + uint32_t delay; + uint32_t anomalous; + + check_tlv_size(TE_LINK_SUBTLV_DEF_SIZE, "Average Link Delay"); + + top = (struct te_link_subtlv_av_delay *)tlvh; + delay = (uint32_t)ntohl(top->value) & TE_EXT_MASK; + anomalous = (uint32_t)ntohl(top->value) & TE_EXT_ANORMAL; + + if (vty != NULL) + vty_out(vty, " %s Average Link Delay: %d (micro-sec)\n", + anomalous ? "Anomalous" : "Normal", delay); + else + zlog_debug(" %s Average Link Delay: %d (micro-sec)", + anomalous ? "Anomalous" : "Normal", delay); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_mm_delay(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_mm_delay *top; + uint32_t low, high; + uint32_t anomalous; + + check_tlv_size(TE_LINK_SUBTLV_MM_DELAY_SIZE, "Min/Max Link Delay"); + + top = (struct te_link_subtlv_mm_delay *)tlvh; + low = (uint32_t)ntohl(top->low) & TE_EXT_MASK; + anomalous = (uint32_t)ntohl(top->low) & TE_EXT_ANORMAL; + high = (uint32_t)ntohl(top->high); + + if (vty != NULL) + vty_out(vty, " %s Min/Max Link Delay: %d/%d (micro-sec)\n", + anomalous ? "Anomalous" : "Normal", low, high); + else + zlog_debug(" %s Min/Max Link Delay: %d/%d (micro-sec)", + anomalous ? "Anomalous" : "Normal", low, high); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_delay_var(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_delay_var *top; + uint32_t jitter; + + check_tlv_size(TE_LINK_SUBTLV_DEF_SIZE, "Link Delay Variation"); + + top = (struct te_link_subtlv_delay_var *)tlvh; + jitter = (uint32_t)ntohl(top->value) & TE_EXT_MASK; + + if (vty != NULL) + vty_out(vty, " Delay Variation: %d (micro-sec)\n", jitter); + else + zlog_debug(" Delay Variation: %d (micro-sec)", jitter); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_pkt_loss(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_pkt_loss *top; + uint32_t loss; + uint32_t anomalous; + float fval; + + check_tlv_size(TE_LINK_SUBTLV_DEF_SIZE, "Link Loss"); + + top = (struct te_link_subtlv_pkt_loss *)tlvh; + loss = (uint32_t)ntohl(top->value) & TE_EXT_MASK; + fval = (float)(loss * LOSS_PRECISION); + anomalous = (uint32_t)ntohl(top->value) & TE_EXT_ANORMAL; + + if (vty != NULL) + vty_out(vty, " %s Link Loss: %g (%%)\n", + anomalous ? "Anomalous" : "Normal", fval); + else + zlog_debug(" %s Link Loss: %g (%%)", + anomalous ? "Anomalous" : "Normal", fval); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_res_bw(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_res_bw *top; + float fval; + + check_tlv_size(TE_LINK_SUBTLV_DEF_SIZE, "Residual Bandwidth"); + + top = (struct te_link_subtlv_res_bw *)tlvh; + fval = ntohf(top->value); + + if (vty != NULL) + vty_out(vty, + " Unidirectional Residual Bandwidth: %g (Bytes/sec)\n", + fval); + else + zlog_debug( + " Unidirectional Residual Bandwidth: %g (Bytes/sec)", + fval); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_ava_bw(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_ava_bw *top; + float fval; + + check_tlv_size(TE_LINK_SUBTLV_DEF_SIZE, "Available Bandwidth"); + + top = (struct te_link_subtlv_ava_bw *)tlvh; + fval = ntohf(top->value); + + if (vty != NULL) + vty_out(vty, + " Unidirectional Available Bandwidth: %g (Bytes/sec)\n", + fval); + else + zlog_debug( + " Unidirectional Available Bandwidth: %g (Bytes/sec)", + fval); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_link_subtlv_use_bw(struct vty *vty, + struct tlv_header *tlvh) +{ + struct te_link_subtlv_use_bw *top; + float fval; + + check_tlv_size(TE_LINK_SUBTLV_DEF_SIZE, "Utilized Bandwidth"); + + top = (struct te_link_subtlv_use_bw *)tlvh; + fval = ntohf(top->value); + + if (vty != NULL) + vty_out(vty, + " Unidirectional Utilized Bandwidth: %g (Bytes/sec)\n", + fval); + else + zlog_debug( + " Unidirectional Utilized Bandwidth: %g (Bytes/sec)", + fval); + + return TLV_SIZE(tlvh); +} + +static uint16_t show_vty_unknown_tlv(struct vty *vty, struct tlv_header *tlvh, + size_t buf_size) +{ + if (TLV_SIZE(tlvh) > buf_size) { + if (vty != NULL) + vty_out(vty, + " TLV size %d exceeds buffer size. Abort!", + TLV_SIZE(tlvh)); + else + zlog_debug( + " TLV size %d exceeds buffer size. Abort!", + TLV_SIZE(tlvh)); + return buf_size; + } + + if (vty != NULL) + vty_out(vty, " Unknown TLV: [type(0x%x), length(0x%x)]\n", + ntohs(tlvh->type), ntohs(tlvh->length)); + else + zlog_debug(" Unknown TLV: [type(0x%x), length(0x%x)]", + ntohs(tlvh->type), ntohs(tlvh->length)); + + return TLV_SIZE(tlvh); +} + +static uint16_t ospf_mpls_te_show_link_subtlv(struct vty *vty, + struct tlv_header *tlvh0, + uint16_t subtotal, uint16_t total) +{ + struct tlv_header *tlvh; + uint16_t sum = subtotal; + + for (tlvh = tlvh0; sum < total; tlvh = TLV_HDR_NEXT(tlvh)) { + switch (ntohs(tlvh->type)) { + case TE_LINK_SUBTLV_LINK_TYPE: + sum += show_vty_link_subtlv_link_type(vty, tlvh); + break; + case TE_LINK_SUBTLV_LINK_ID: + sum += show_vty_link_subtlv_link_id(vty, tlvh); + break; + case TE_LINK_SUBTLV_LCLIF_IPADDR: + sum += show_vty_link_subtlv_lclif_ipaddr(vty, tlvh, + total - sum); + break; + case TE_LINK_SUBTLV_RMTIF_IPADDR: + sum += show_vty_link_subtlv_rmtif_ipaddr(vty, tlvh, + total - sum); + break; + case TE_LINK_SUBTLV_TE_METRIC: + sum += show_vty_link_subtlv_te_metric(vty, tlvh); + break; + case TE_LINK_SUBTLV_MAX_BW: + sum += show_vty_link_subtlv_max_bw(vty, tlvh); + break; + case TE_LINK_SUBTLV_MAX_RSV_BW: + sum += show_vty_link_subtlv_max_rsv_bw(vty, tlvh); + break; + case TE_LINK_SUBTLV_UNRSV_BW: + sum += show_vty_link_subtlv_unrsv_bw(vty, tlvh); + break; + case TE_LINK_SUBTLV_RSC_CLSCLR: + sum += show_vty_link_subtlv_rsc_clsclr(vty, tlvh); + break; + case TE_LINK_SUBTLV_LRRID: + sum += show_vty_link_subtlv_lrrid(vty, tlvh); + break; + case TE_LINK_SUBTLV_LLRI: + sum += show_vty_link_subtlv_llri(vty, tlvh); + break; + case TE_LINK_SUBTLV_RIP: + sum += show_vty_link_subtlv_rip(vty, tlvh); + break; + case TE_LINK_SUBTLV_RAS: + sum += show_vty_link_subtlv_ras(vty, tlvh); + break; + case TE_LINK_SUBTLV_AV_DELAY: + sum += show_vty_link_subtlv_av_delay(vty, tlvh); + break; + case TE_LINK_SUBTLV_MM_DELAY: + sum += show_vty_link_subtlv_mm_delay(vty, tlvh); + break; + case TE_LINK_SUBTLV_DELAY_VAR: + sum += show_vty_link_subtlv_delay_var(vty, tlvh); + break; + case TE_LINK_SUBTLV_PKT_LOSS: + sum += show_vty_link_subtlv_pkt_loss(vty, tlvh); + break; + case TE_LINK_SUBTLV_RES_BW: + sum += show_vty_link_subtlv_res_bw(vty, tlvh); + break; + case TE_LINK_SUBTLV_AVA_BW: + sum += show_vty_link_subtlv_ava_bw(vty, tlvh); + break; + case TE_LINK_SUBTLV_USE_BW: + sum += show_vty_link_subtlv_use_bw(vty, tlvh); + break; + default: + sum += show_vty_unknown_tlv(vty, tlvh, total - sum); + break; + } + } + return sum; +} + +static void ospf_mpls_te_show_info(struct vty *vty, struct json_object *json, + struct ospf_lsa *lsa) +{ + struct lsa_header *lsah = lsa->data; + struct tlv_header *tlvh, *next; + uint16_t sum, total; + uint16_t (*subfunc)(struct vty * vty, struct tlv_header * tlvh, + uint16_t subtotal, uint16_t total) = NULL; + + if (json) + return; + + sum = 0; + total = lsa->size - OSPF_LSA_HEADER_SIZE; + + for (tlvh = TLV_HDR_TOP(lsah); sum < total && tlvh; + tlvh = (next ? next : TLV_HDR_NEXT(tlvh))) { + if (subfunc != NULL) { + sum = (*subfunc)(vty, tlvh, sum, total); + next = (struct tlv_header *)((char *)tlvh + sum); + subfunc = NULL; + continue; + } + + next = NULL; + switch (ntohs(tlvh->type)) { + case TE_TLV_ROUTER_ADDR: + sum += show_vty_router_addr(vty, tlvh); + break; + case TE_TLV_LINK: + sum += show_vty_link_header(vty, tlvh, total - sum); + subfunc = ospf_mpls_te_show_link_subtlv; + next = TLV_DATA(tlvh); + break; + default: + sum += show_vty_unknown_tlv(vty, tlvh, total - sum); + break; + } + } + return; +} + +static void ospf_mpls_te_config_write_router(struct vty *vty) +{ + + if (OspfMplsTE.enabled) { + vty_out(vty, " mpls-te on\n"); + vty_out(vty, " mpls-te router-address %pI4\n", + &OspfMplsTE.router_addr.value); + + if (OspfMplsTE.inter_as == AS) + vty_out(vty, " mpls-te inter-as as\n"); + if (OspfMplsTE.inter_as == Area) + vty_out(vty, " mpls-te inter-as area %pI4 \n", + &OspfMplsTE.interas_areaid); + if (OspfMplsTE.export) + vty_out(vty, " mpls-te export\n"); + } + return; +} + +/*------------------------------------------------------------------------* + * Following are vty command functions. + *------------------------------------------------------------------------*/ + +DEFUN (ospf_mpls_te_on, + ospf_mpls_te_on_cmd, + "mpls-te on", + MPLS_TE_STR + "Enable the MPLS-TE functionality\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct listnode *node; + struct mpls_te_link *lp; + + if (OspfMplsTE.enabled) + return CMD_SUCCESS; + + /* Check that the OSPF is using default VRF */ + if (ospf->vrf_id != VRF_DEFAULT) { + vty_out(vty, "MPLS TE is only supported in default VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ote_debug("MPLS-TE: OFF -> ON"); + + OspfMplsTE.enabled = true; + + /* Reoriginate RFC3630 & RFC6827 Links */ + ospf_mpls_te_foreach_area(ospf_mpls_te_lsa_schedule, + REORIGINATE_THIS_LSA); + + /* Reoriginate LSA if INTER-AS is always on */ + if (OspfMplsTE.inter_as != Off) { + for (ALL_LIST_ELEMENTS_RO(OspfMplsTE.iflist, node, lp)) { + if (IS_INTER_AS(lp->type)) { + ospf_mpls_te_lsa_schedule(lp, + REORIGINATE_THIS_LSA); + } + } + } + + /* Create TED and initialize it */ + OspfMplsTE.ted = ls_ted_new(1, "OSPF", 0); + if (!OspfMplsTE.ted) { + vty_out(vty, "Unable to create Link State Data Base\n"); + return CMD_WARNING; + } + ospf_te_init_ted(OspfMplsTE.ted, ospf); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_mpls_te, + no_ospf_mpls_te_cmd, + "no mpls-te [on]", + NO_STR + MPLS_TE_STR + "Disable the MPLS-TE functionality\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct listnode *node, *nnode; + struct mpls_te_link *lp; + + if (!OspfMplsTE.enabled) + return CMD_SUCCESS; + + ote_debug("MPLS-TE: ON -> OFF"); + + /* Remove TED */ + ls_ted_del_all(&OspfMplsTE.ted); + OspfMplsTE.enabled = false; + + /* Flush all TE Opaque LSAs */ + for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) + if (CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) + ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); + + /* + * This resets the OspfMplsTE.inter_as to its initial state. + * This is to avoid having an inter-as value different from + * Off when mpls-te gets restarted (after being removed) + */ + OspfMplsTE.inter_as = Off; + + return CMD_SUCCESS; +} + +DEFUN (ospf_mpls_te_router_addr, + ospf_mpls_te_router_addr_cmd, + "mpls-te router-address A.B.C.D", + MPLS_TE_STR + "Stable IP address of the advertising router\n" + "MPLS-TE router address in IPv4 address format\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4 = 2; + struct te_tlv_router_addr *ra = &OspfMplsTE.router_addr; + struct in_addr value; + + if (!inet_aton(argv[idx_ipv4]->arg, &value)) { + vty_out(vty, "Please specify Router-Addr by A.B.C.D\n"); + return CMD_WARNING; + } + + if (ntohs(ra->header.type) == 0 + || ntohl(ra->value.s_addr) != ntohl(value.s_addr)) { + struct listnode *node, *nnode; + struct mpls_te_link *lp; + int need_to_reoriginate = 0; + + set_mpls_te_router_addr(value); + + if (!OspfMplsTE.enabled) + return CMD_SUCCESS; + + for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) { + if ((lp->area == NULL) || IS_FLOOD_AS(lp->flags)) + continue; + + if (!CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) { + need_to_reoriginate = 1; + break; + } + } + + for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) { + if ((lp->area == NULL) || IS_FLOOD_AS(lp->flags)) + continue; + + if (need_to_reoriginate) + SET_FLAG(lp->flags, LPFLG_LSA_FORCED_REFRESH); + else + ospf_mpls_te_lsa_schedule(lp, REFRESH_THIS_LSA); + } + + if (need_to_reoriginate) + ospf_mpls_te_foreach_area(ospf_mpls_te_lsa_schedule, + REORIGINATE_THIS_LSA); + } + + return CMD_SUCCESS; +} + +static int set_inter_as_mode(struct vty *vty, const char *mode_name, + const char *area_id) +{ + enum inter_as_mode mode; + struct listnode *node; + struct mpls_te_link *lp; + int format; + + if (OspfMplsTE.enabled) { + + /* Read and Check inter_as mode */ + if (strcmp(mode_name, "as") == 0) + mode = AS; + else if (strcmp(mode_name, "area") == 0) { + mode = Area; + VTY_GET_OSPF_AREA_ID(OspfMplsTE.interas_areaid, format, + area_id); + } else { + vty_out(vty, + "Unknown mode. Please choose between as or area\n"); + return CMD_WARNING; + } + + ote_debug( + "MPLS-TE (%s): Inter-AS enable with %s flooding support", + __func__, mode2text[mode]); + + /* Enable mode and re-originate LSA if needed */ + if ((OspfMplsTE.inter_as == Off) + && (mode != OspfMplsTE.inter_as)) { + OspfMplsTE.inter_as = mode; + /* Re-originate all InterAS-TEv2 LSA */ + for (ALL_LIST_ELEMENTS_RO(OspfMplsTE.iflist, node, + lp)) { + if (IS_INTER_AS(lp->type)) { + if (mode == AS) + SET_FLAG(lp->flags, + LPFLG_LSA_FLOOD_AS); + else + UNSET_FLAG(lp->flags, + LPFLG_LSA_FLOOD_AS); + ospf_mpls_te_lsa_schedule( + lp, REORIGINATE_THIS_LSA); + } + } + } else { + vty_out(vty, + "Please change Inter-AS support to disable first before going to mode %s\n", + mode2text[mode]); + return CMD_WARNING; + } + } else { + vty_out(vty, "mpls-te has not been turned on\n"); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + + +DEFUN (ospf_mpls_te_inter_as_as, + ospf_mpls_te_inter_as_cmd, + "mpls-te inter-as as", + MPLS_TE_STR + "Configure MPLS-TE Inter-AS support\n" + "AS native mode self originate INTER_AS LSA with Type 11 (as flooding scope)\n") +{ + return set_inter_as_mode(vty, "as", ""); +} + +DEFUN (ospf_mpls_te_inter_as_area, + ospf_mpls_te_inter_as_area_cmd, + "mpls-te inter-as area ", + MPLS_TE_STR + "Configure MPLS-TE Inter-AS support\n" + "AREA native mode self originate INTER_AS LSA with Type 10 (area flooding scope)\n" + "OSPF area ID in IP format\n" + "OSPF area ID as decimal value\n") +{ + int idx_ipv4_number = 3; + return set_inter_as_mode(vty, "area", argv[idx_ipv4_number]->arg); +} + +DEFUN (no_ospf_mpls_te_inter_as, + no_ospf_mpls_te_inter_as_cmd, + "no mpls-te inter-as", + NO_STR + MPLS_TE_STR + "Disable MPLS-TE Inter-AS support\n") +{ + + struct listnode *node, *nnode; + struct mpls_te_link *lp; + + ote_debug("MPLS-TE: Inter-AS support OFF"); + + if ((OspfMplsTE.enabled) && (OspfMplsTE.inter_as != Off)) { + /* Flush all Inter-AS LSA */ + for (ALL_LIST_ELEMENTS(OspfMplsTE.iflist, node, nnode, lp)) + if (IS_INTER_AS(lp->type) + && CHECK_FLAG(lp->flags, LPFLG_LSA_ENGAGED)) + ospf_mpls_te_lsa_schedule(lp, FLUSH_THIS_LSA); + + OspfMplsTE.inter_as = Off; + } + + return CMD_SUCCESS; +} + +DEFUN (ospf_mpls_te_export, + ospf_mpls_te_export_cmd, + "mpls-te export", + MPLS_TE_STR + "Export the MPLS-TE information as Link State\n") +{ + + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + if (OspfMplsTE.enabled) { + if (ls_register(zclient, true) != 0) { + vty_out(vty, "Unable to register Link State\n"); + return CMD_WARNING; + } + OspfMplsTE.export = true; + } else { + vty_out(vty, "mpls-te has not been turned on\n"); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + + +DEFUN (no_ospf_mpls_te_export, + no_ospf_mpls_te_export_cmd, + "no mpls-te export", + NO_STR + MPLS_TE_STR + "Stop export of the MPLS-TE information as Link State\n") +{ + + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + if (OspfMplsTE.export) { + if (ls_unregister(zclient, true) != 0) { + vty_out(vty, "Unable to unregister Link State\n"); + return CMD_WARNING; + } + OspfMplsTE.export = false; + } + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_mpls_te_router, + show_ip_ospf_mpls_te_router_cmd, + "show ip ospf mpls-te router", + SHOW_STR + IP_STR + OSPF_STR + "MPLS-TE information\n" + "MPLS-TE Router parameters\n") +{ + if (OspfMplsTE.enabled) { + vty_out(vty, "--- MPLS-TE router parameters ---\n"); + + if (ntohs(OspfMplsTE.router_addr.header.type) != 0) + show_vty_router_addr(vty, + &OspfMplsTE.router_addr.header); + else + vty_out(vty, " Router address is not set\n"); + vty_out(vty, " Link State distribution is %s\n", + OspfMplsTE.export ? "Active" : "Inactive"); + } + return CMD_SUCCESS; +} + +static void show_mpls_te_link_sub(struct vty *vty, struct interface *ifp) +{ + struct mpls_te_link *lp; + + if ((OspfMplsTE.enabled) && HAS_LINK_PARAMS(ifp) && !if_is_loopback(ifp) + && if_is_up(ifp) + && ((lp = lookup_linkparams_by_ifp(ifp)) != NULL)) { + /* Continue only if interface is not passive or support Inter-AS + * TEv2 */ + if (!(ospf_oi_count(ifp) > 0)) { + if (IS_INTER_AS(lp->type)) { + vty_out(vty, + "-- Inter-AS TEv2 link parameters for %s --\n", + ifp->name); + } else { + /* MPLS-TE is not activate on this interface */ + /* or this interface is passive and Inter-AS + * TEv2 is not activate */ + vty_out(vty, + " %s: MPLS-TE is disabled on this interface\n", + ifp->name); + return; + } + } else { + vty_out(vty, "-- MPLS-TE link parameters for %s --\n", + ifp->name); + } + + if (TLV_TYPE(lp->link_type) != 0) + show_vty_link_subtlv_link_type(vty, + &lp->link_type.header); + if (TLV_TYPE(lp->link_id) != 0) + show_vty_link_subtlv_link_id(vty, &lp->link_id.header); + if (TLV_TYPE(lp->lclif_ipaddr) != 0) + show_vty_link_subtlv_lclif_ipaddr( + vty, &lp->lclif_ipaddr.header, + lp->lclif_ipaddr.header.length); + if (TLV_TYPE(lp->rmtif_ipaddr) != 0) + show_vty_link_subtlv_rmtif_ipaddr( + vty, &lp->rmtif_ipaddr.header, + lp->rmtif_ipaddr.header.length); + if (TLV_TYPE(lp->rip) != 0) + show_vty_link_subtlv_rip(vty, &lp->rip.header); + if (TLV_TYPE(lp->ras) != 0) + show_vty_link_subtlv_ras(vty, &lp->ras.header); + if (TLV_TYPE(lp->te_metric) != 0) + show_vty_link_subtlv_te_metric(vty, + &lp->te_metric.header); + if (TLV_TYPE(lp->max_bw) != 0) + show_vty_link_subtlv_max_bw(vty, &lp->max_bw.header); + if (TLV_TYPE(lp->max_rsv_bw) != 0) + show_vty_link_subtlv_max_rsv_bw(vty, + &lp->max_rsv_bw.header); + if (TLV_TYPE(lp->unrsv_bw) != 0) + show_vty_link_subtlv_unrsv_bw(vty, + &lp->unrsv_bw.header); + if (TLV_TYPE(lp->rsc_clsclr) != 0) + show_vty_link_subtlv_rsc_clsclr(vty, + &lp->rsc_clsclr.header); + if (TLV_TYPE(lp->av_delay) != 0) + show_vty_link_subtlv_av_delay(vty, + &lp->av_delay.header); + if (TLV_TYPE(lp->mm_delay) != 0) + show_vty_link_subtlv_mm_delay(vty, + &lp->mm_delay.header); + if (TLV_TYPE(lp->delay_var) != 0) + show_vty_link_subtlv_delay_var(vty, + &lp->delay_var.header); + if (TLV_TYPE(lp->pkt_loss) != 0) + show_vty_link_subtlv_pkt_loss(vty, + &lp->pkt_loss.header); + if (TLV_TYPE(lp->res_bw) != 0) + show_vty_link_subtlv_res_bw(vty, &lp->res_bw.header); + if (TLV_TYPE(lp->ava_bw) != 0) + show_vty_link_subtlv_ava_bw(vty, &lp->ava_bw.header); + if (TLV_TYPE(lp->use_bw) != 0) + show_vty_link_subtlv_use_bw(vty, &lp->use_bw.header); + vty_out(vty, "---------------\n\n"); + } else { + vty_out(vty, " %s: MPLS-TE is disabled on this interface\n", + ifp->name); + } + + return; +} + +DEFUN (show_ip_ospf_mpls_te_link, + show_ip_ospf_mpls_te_link_cmd, + "show ip ospf mpls-te interface [INTERFACE]", + SHOW_STR + IP_STR + OSPF_STR + "MPLS-TE information\n" + "Interface information\n" + "Interface name\n") +{ + struct vrf *vrf; + int idx_interface = 0; + struct interface *ifp = NULL; + struct ospf *ospf = NULL; + + argv_find(argv, argc, "INTERFACE", &idx_interface); + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || !ospf->oi_running) + return CMD_SUCCESS; + + vrf = vrf_lookup_by_id(VRF_DEFAULT); + if (!vrf) + return CMD_SUCCESS; + if (idx_interface) { + ifp = if_lookup_by_name(argv[idx_interface]->arg, VRF_DEFAULT); + if (ifp == NULL) { + vty_out(vty, "No such interface name in vrf %s\n", + vrf->name); + return CMD_SUCCESS; + } + } + if (!ifp) { + FOR_ALL_INTERFACES (vrf, ifp) + show_mpls_te_link_sub(vty, ifp); + return CMD_SUCCESS; + } + + show_mpls_te_link_sub(vty, ifp); + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_mpls_te_db, + show_ip_ospf_mpls_te_db_cmd, + "show ip ospf mpls-te database []|edge [A.B.C.D]|subnet [A.B.C.D/M]>] [verbose|json]", + SHOW_STR + IP_STR + OSPF_STR + "MPLS-TE information\n" + "MPLS-TE database\n" + "MPLS-TE Vertex\n" + "Self-originated MPLS-TE router\n" + "Advertised MPLS-TE router\n" + "MPLS-TE router ID (as an IP address)\n" + "MPLS-TE Edge\n" + "MPLS-TE Edge ID (as an IP address)\n" + "MPLS-TE Subnet\n" + "MPLS-TE Subnet ID (as an IP prefix)\n" + "Verbose output\n" + JSON_STR) +{ + int idx = 0; + struct in_addr ip_addr; + struct prefix pref; + struct ls_vertex *vertex; + struct ls_edge *edge; + struct ls_subnet *subnet; + uint64_t key; + struct ls_edge_key ekey; + bool verbose = false; + bool uj = use_json(argc, argv); + json_object *json = NULL; + + if (!OspfMplsTE.enabled || !OspfMplsTE.ted) { + vty_out(vty, "MPLS-TE database is not enabled\n"); + return CMD_WARNING; + } + + if (uj) + json = json_object_new_object(); + + if (argv[argc - 1]->arg && strmatch(argv[argc - 1]->text, "verbose")) + verbose = true; + + idx = 5; + if (argv_find(argv, argc, "vertex", &idx)) { + /* Show Vertex */ + if (argv_find(argv, argc, "self-originate", &idx)) + vertex = OspfMplsTE.ted->self; + else if (argv_find(argv, argc, "adv-router", &idx)) { + if (!inet_aton(argv[idx + 1]->arg, &ip_addr)) { + vty_out(vty, + "Specified Router ID %s is invalid\n", + argv[idx + 1]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + /* Get the Vertex from the Link State Database */ + key = ((uint64_t)ntohl(ip_addr.s_addr)) & 0xffffffff; + vertex = ls_find_vertex_by_key(OspfMplsTE.ted, key); + if (!vertex) { + vty_out(vty, "No vertex found for ID %pI4\n", + &ip_addr); + return CMD_WARNING; + } + } else + vertex = NULL; + + if (vertex) + ls_show_vertex(vertex, vty, json, verbose); + else + ls_show_vertices(OspfMplsTE.ted, vty, json, verbose); + + } else if (argv_find(argv, argc, "edge", &idx)) { + /* Show Edge */ + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &ip_addr)) { + vty_out(vty, + "Specified Edge ID %s is invalid\n", + argv[idx]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + /* Get the Edge from the Link State Database */ + ekey.family = AF_INET; + IPV4_ADDR_COPY(&ekey.k.addr, &ip_addr); + edge = ls_find_edge_by_key(OspfMplsTE.ted, ekey); + if (!edge) { + vty_out(vty, "No edge found for ID %pI4\n", + &ip_addr); + return CMD_WARNING; + } + } else + edge = NULL; + + if (edge) + ls_show_edge(edge, vty, json, verbose); + else + ls_show_edges(OspfMplsTE.ted, vty, json, verbose); + + } else if (argv_find(argv, argc, "subnet", &idx)) { + /* Show Subnet */ + if (argv_find(argv, argc, "A.B.C.D/M", &idx)) { + if (!str2prefix(argv[idx]->arg, &pref)) { + vty_out(vty, "Invalid prefix format %s\n", + argv[idx]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + /* Get the Subnet from the Link State Database */ + subnet = ls_find_subnet(OspfMplsTE.ted, &pref); + if (!subnet) { + vty_out(vty, "No subnet found for ID %pFX\n", + &pref); + return CMD_WARNING; + } + } else + subnet = NULL; + + if (subnet) + ls_show_subnet(subnet, vty, json, verbose); + else + ls_show_subnets(OspfMplsTE.ted, vty, json, verbose); + + } else { + /* Show the complete TED */ + ls_show_ted(OspfMplsTE.ted, vty, json, verbose); + } + + if (uj) + vty_json(vty, json); + return CMD_SUCCESS; +} + +static void ospf_mpls_te_register_vty(void) +{ + install_element(VIEW_NODE, &show_ip_ospf_mpls_te_router_cmd); + install_element(VIEW_NODE, &show_ip_ospf_mpls_te_link_cmd); + install_element(VIEW_NODE, &show_ip_ospf_mpls_te_db_cmd); + + install_element(OSPF_NODE, &ospf_mpls_te_on_cmd); + install_element(OSPF_NODE, &no_ospf_mpls_te_cmd); + install_element(OSPF_NODE, &ospf_mpls_te_router_addr_cmd); + install_element(OSPF_NODE, &ospf_mpls_te_inter_as_cmd); + install_element(OSPF_NODE, &ospf_mpls_te_inter_as_area_cmd); + install_element(OSPF_NODE, &no_ospf_mpls_te_inter_as_cmd); + install_element(OSPF_NODE, &ospf_mpls_te_export_cmd); + install_element(OSPF_NODE, &no_ospf_mpls_te_export_cmd); + + return; +} diff --git a/ospfd/ospf_te.h b/ospfd/ospf_te.h new file mode 100644 index 0000000..49906ef --- /dev/null +++ b/ospfd/ospf_te.h @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This is an implementation of RFC3630, RFC5392 & RFC6827 + * Copyright (C) 2001 KDD R&D Laboratories, Inc. + * http://www.kddlabs.co.jp/ + * + * Copyright (C) 2012 Orange Labs + * http://www.orange.com + */ + +/* Add support of RFC7471 */ +/* Add support of RFC5392 */ +/* Add support of RFC6827 (partial) */ + +#ifndef _ZEBRA_OSPF_MPLS_TE_H +#define _ZEBRA_OSPF_MPLS_TE_H + +/* + * Opaque LSA's link state ID for Traffic Engineering is + * structured as follows. + * + * 24 16 8 0 + * +--------+--------+--------+--------+ + * | 1 | MBZ |........|........| + * +--------+--------+--------+--------+ + * |<-Type->||<-- Instance --->| + * + * + * Type: IANA has assigned '1' for Traffic Engineering. + * MBZ: Reserved, must be set to zero. + * Instance: User may select an arbitrary 16-bit value. + * + */ + +#define MAX_LEGAL_TE_INSTANCE_NUM (0xffff) +#define LEGAL_TE_INSTANCE_RANGE(i) (0 <= (i) && (i) <= 0xffff) + +/* + * 24 16 8 0 + * +--------+--------+--------+--------+ --- + * | LS age |Options | 10 | A + * +--------+--------+--------+--------+ | + * | 1 | 0 | Instance | | + * +--------+--------+--------+--------+ | + * | Advertising router | | Standard (Opaque) LSA header; + * +--------+--------+--------+--------+ | Only type-10 is used. + * | LS sequence number | | + * +--------+--------+--------+--------+ | + * | LS checksum | Length | V + * +--------+--------+--------+--------+ --- + * | Type | Length | A + * +--------+--------+--------+--------+ | TLV part for TE; Values might be + * | Values ... | V structured as a set of sub-TLVs. + * +--------+--------+--------+--------+ --- + */ + +/* Following define the type of TE link regarding the various RFC */ +#define STD_TE 0x01 +#define GMPLS 0x02 +#define INTER_AS 0x04 +#define PSEUDO_TE 0x08 +#define EMULATED 0x10 + +#define IS_STD_TE(x) (x & STD_TE) +#define IS_PSEUDO_TE(x) (x & PSEUDO_TE) +#define IS_INTER_AS(x) (x & INTER_AS) +#define IS_EMULATED(x) (x & EMULATED) + +/* Flags to manage TE Link LSA */ +#define LPFLG_LSA_INACTIVE 0x00 +#define LPFLG_LSA_ACTIVE 0x01 +#define LPFLG_LSA_ENGAGED 0x02 +#define LPFLG_LOOKUP_DONE 0x04 +#define LPFLG_LSA_FORCED_REFRESH 0x08 +#define LPFLG_LSA_FLOOD_AS 0x10 + +#define IS_FLOOD_AS(x) (x & LPFLG_LSA_FLOOD_AS) + +/* Macro to log debug message */ +#define ote_debug(...) \ + do { \ + if (IS_DEBUG_OSPF_TE) \ + zlog_debug(__VA_ARGS__); \ + } while (0) + +/* + * Following section defines TLV body parts. + */ + +/* Router Address TLV */ /* Mandatory */ +#define TE_TLV_ROUTER_ADDR 1 +struct te_tlv_router_addr { + struct tlv_header header; /* Value length is 4 octets. */ + struct in_addr value; +}; + +/* Link TLV */ +#define TE_TLV_LINK 2 +struct te_tlv_link { + struct tlv_header header; + /* A set of link-sub-TLVs will follow. */ +}; + +/* Default TE TLV size */ +#define TE_LINK_SUBTLV_DEF_SIZE 4 + +/* Link Type Sub-TLV */ /* Mandatory */ +#define TE_LINK_SUBTLV_LINK_TYPE 1 +#define TE_LINK_SUBTLV_TYPE_SIZE 1 +struct te_link_subtlv_link_type { + struct tlv_header header; /* Value length is 1 octet. */ + struct { +#define LINK_TYPE_SUBTLV_VALUE_PTP 1 +#define LINK_TYPE_SUBTLV_VALUE_MA 2 + uint8_t value; + uint8_t padding[3]; + } link_type; +}; + +/* Link Sub-TLV: Link ID */ /* Mandatory */ +#define TE_LINK_SUBTLV_LINK_ID 2 +struct te_link_subtlv_link_id { + struct tlv_header header; /* Value length is 4 octets. */ + struct in_addr value; /* Same as router-lsa's link-id. */ +}; + +/* Link Sub-TLV: Local Interface IP Address */ /* Optional */ +#define TE_LINK_SUBTLV_LCLIF_IPADDR 3 +struct te_link_subtlv_lclif_ipaddr { + struct tlv_header header; /* Value length is 4 x N octets. */ + struct in_addr value[1]; /* Local IP address(es). */ +}; + +/* Link Sub-TLV: Remote Interface IP Address */ /* Optional */ +#define TE_LINK_SUBTLV_RMTIF_IPADDR 4 +struct te_link_subtlv_rmtif_ipaddr { + struct tlv_header header; /* Value length is 4 x N octets. */ + struct in_addr value[1]; /* Neighbor's IP address(es). */ +}; + +/* Link Sub-TLV: Traffic Engineering Metric */ /* Optional */ +#define TE_LINK_SUBTLV_TE_METRIC 5 +struct te_link_subtlv_te_metric { + struct tlv_header header; /* Value length is 4 octets. */ + uint32_t value; /* Link metric for TE purpose. */ +}; + +/* Link Sub-TLV: Maximum Bandwidth */ /* Optional */ +#define TE_LINK_SUBTLV_MAX_BW 6 +struct te_link_subtlv_max_bw { + struct tlv_header header; /* Value length is 4 octets. */ + float value; /* bytes/sec */ +}; + +/* Link Sub-TLV: Maximum Reservable Bandwidth */ /* Optional */ +#define TE_LINK_SUBTLV_MAX_RSV_BW 7 +struct te_link_subtlv_max_rsv_bw { + struct tlv_header header; /* Value length is 4 octets. */ + float value; /* bytes/sec */ +}; + +/* Link Sub-TLV: Unreserved Bandwidth */ /* Optional */ +#define TE_LINK_SUBTLV_UNRSV_BW 8 +#define TE_LINK_SUBTLV_UNRSV_SIZE 32 +struct te_link_subtlv_unrsv_bw { + struct tlv_header header; /* Value length is 32 octets. */ + float value[MAX_CLASS_TYPE]; /* One for each priority level. */ +}; + +/* Link Sub-TLV: Resource Class/Color */ /* Optional */ +#define TE_LINK_SUBTLV_RSC_CLSCLR 9 +struct te_link_subtlv_rsc_clsclr { + struct tlv_header header; /* Value length is 4 octets. */ + uint32_t value; /* Admin. group membership. */ +}; + +/* For RFC6827 */ +/* Local and Remote TE Router ID */ +#define TE_LINK_SUBTLV_LRRID 10 +#define TE_LINK_SUBTLV_LRRID_SIZE 8 +struct te_link_subtlv_lrrid { + struct tlv_header header; /* Value length is 8 octets. */ + struct in_addr local; /* Local TE Router Identifier */ + struct in_addr remote; /* Remote TE Router Identifier */ +}; + +/* RFC4203: Link Local/Remote Identifiers */ +#define TE_LINK_SUBTLV_LLRI 11 +#define TE_LINK_SUBTLV_LLRI_SIZE 8 +struct te_link_subtlv_llri { + struct tlv_header header; /* Value length is 8 octets. */ + uint32_t local; /* Link Local Identifier */ + uint32_t remote; /* Link Remote Identifier */ +}; + +/* Inter-RA Export Upward sub-TLV (12) and Inter-RA Export Downward sub-TLV (13) + * (RFC6827bis) are not yet supported */ +/* SUBTLV 14-16 (RFC4203) are not yet supported */ +/* Bandwidth Constraints sub-TLV (17) (RFC4124) is not yet supported */ +/* SUBLV 18-20 are for OSPFv3 TE (RFC5329). see ospf6d */ + +/* For RFC 5392 */ +/* Remote AS Number sub-TLV */ +#define TE_LINK_SUBTLV_RAS 21 +struct te_link_subtlv_ras { + struct tlv_header header; /* Value length is 4 octets. */ + uint32_t value; /* Remote AS number */ +}; + +/* IPv4 Remote ASBR ID Sub-TLV */ +#define TE_LINK_SUBTLV_RIP 22 +struct te_link_subtlv_rip { + struct tlv_header header; /* Value length is 4 octets. */ + struct in_addr value; /* Remote ASBR IP address */ +}; + +/* SUBTLV 24 is IPv6 Remote ASBR ID (RFC5392). see ospf6d */ + +/* SUBTLV 23 (RFC5330) and 25 (RFC6001) are not yet supported */ + +/* SUBTLV 26 (RFC7308) is not yet supported */ + +/* RFC7471 */ +/* Link Sub-TLV: Average Link Delay */ /* Optional */ +#define TE_LINK_SUBTLV_AV_DELAY 27 +struct te_link_subtlv_av_delay { + struct tlv_header header; /* Value length is 4 bytes. */ + /* + * delay in micro-seconds only 24 bits => 0 ... 16777215 + * with Anomalous Bit as Upper most bit + */ + uint32_t value; +}; + +/* Link Sub-TLV: Low/High Link Delay */ +#define TE_LINK_SUBTLV_MM_DELAY 28 +#define TE_LINK_SUBTLV_MM_DELAY_SIZE 8 +struct te_link_subtlv_mm_delay { + struct tlv_header header; /* Value length is 8 bytes. */ + /* + * low delay in micro-seconds only 24 bits => 0 ... 16777215 + * with Anomalous Bit (A) as Upper most bit + */ + uint32_t low; + /* high delay in micro-seconds only 24 bits => 0 ... 16777215 */ + uint32_t high; +}; + +/* Link Sub-TLV: Link Delay Variation i.e. Jitter */ +#define TE_LINK_SUBTLV_DELAY_VAR 29 +struct te_link_subtlv_delay_var { + struct tlv_header header; /* Value length is 4 bytes. */ + /* interval in micro-seconds only 24 bits => 0 ... 16777215 */ + uint32_t value; +}; + +/* Link Sub-TLV: Routine Unidirectional Link Packet Loss */ +#define TE_LINK_SUBTLV_PKT_LOSS 30 +struct te_link_subtlv_pkt_loss { + struct tlv_header header; /* Value length is 4 bytes. */ + /* + * in percentage of total traffic only 24 bits (2^24 - 2) + * with Anomalous Bit as Upper most bit + */ + uint32_t value; +}; + +/* Link Sub-TLV: Unidirectional Residual Bandwidth */ /* Optional */ +#define TE_LINK_SUBTLV_RES_BW 31 +struct te_link_subtlv_res_bw { + struct tlv_header header; /* Value length is 4 bytes. */ + /* bandwidth in IEEE floating point format with units in bytes/second */ + float value; +}; + +/* Link Sub-TLV: Unidirectional Available Bandwidth */ /* Optional */ +#define TE_LINK_SUBTLV_AVA_BW 32 +struct te_link_subtlv_ava_bw { + struct tlv_header header; /* Value length is 4 octets. */ + /* bandwidth in IEEE floating point format with units in bytes/second */ + float value; +}; + +/* Link Sub-TLV: Unidirectional Utilized Bandwidth */ /* Optional */ +#define TE_LINK_SUBTLV_USE_BW 33 +struct te_link_subtlv_use_bw { + struct tlv_header header; /* Value length is 4 octets. */ + /* bandwidth in IEEE floating point format with units in bytes/second */ + float value; +}; + +#define TE_LINK_SUBTLV_MAX 34 /* Last SUBTLV + 1 */ + +/* Here are "non-official" architectural constants. */ +#define MPLS_TE_MINIMUM_BANDWIDTH 1.0 /* Reasonable? *//* XXX */ + +/* Mode for Inter-AS Opaque-LSA */ +enum inter_as_mode { Off, AS, Area }; + +struct te_link_subtlv { + struct tlv_header header; + union { + uint32_t link_type; + struct in_addr link_id; + struct in_addr lclif; + struct in_addr rmtif; + uint32_t te_metric; + float max_bw; + float max_rsv_bw; + float unrsv[8]; + uint32_t rsc_clsclr; + uint32_t llri[2]; + uint32_t ras; + struct in_addr rip; + struct in_addr lrrid[2]; + uint32_t av_delay; + uint32_t mm_delay; + uint32_t delay_var; + uint32_t pkt_loss; + float res_bw; + float ava_bw; + float use_bw; + } value; +}; + +/* Following structure are internal use only. */ +struct ospf_mpls_te { + /* Status of MPLS-TE: enable or disable */ + bool enabled; + + /* Traffic Engineering Database i.e. Link State */ + struct ls_ted *ted; + bool export; + + /* RFC5392 */ + enum inter_as_mode inter_as; + struct in_addr interas_areaid; + + /* List elements are zebra-interfaces (ifp), not ospf-interfaces (oi). + */ + struct list *iflist; + + /* Store Router-TLV in network byte order. */ + struct te_tlv_router_addr router_addr; +}; + +struct mpls_te_link { + /* + * According to MPLS-TE (draft) specification, 24-bit Opaque-ID field + * is subdivided into 8-bit "unused" field and 16-bit "instance" field. + * In this implementation, each Link-TLV has its own instance. + */ + uint32_t instance; + + /* Reference pointer to a Zebra-interface. */ + struct interface *ifp; + + /* Area info in which this MPLS-TE link belongs to. */ + struct ospf_area *area; + + /* Flags to manage this link parameters. */ + uint32_t flags; + + /* Type of MPLS-TE link: RFC3630, RFC5392, RFC5392 emulated, RFC6827 */ + uint8_t type; + + /* Store Link-TLV in network byte order. */ + /* RFC3630 & RFC6827 / RFC 6827 */ + struct te_tlv_link link_header; + struct te_link_subtlv_link_type link_type; + struct te_link_subtlv_link_id link_id; + struct te_link_subtlv_lclif_ipaddr lclif_ipaddr; + struct te_link_subtlv_rmtif_ipaddr rmtif_ipaddr; + struct te_link_subtlv_te_metric te_metric; + struct te_link_subtlv_max_bw max_bw; + struct te_link_subtlv_max_rsv_bw max_rsv_bw; + struct te_link_subtlv_unrsv_bw unrsv_bw; + struct te_link_subtlv_rsc_clsclr rsc_clsclr; + /* RFC4203 */ + struct te_link_subtlv_llri llri; + /* RFC5392 */ + struct te_link_subtlv_ras ras; + struct te_link_subtlv_rip rip; + /* RFC6827 */ + struct te_link_subtlv_lrrid lrrid; + /* RFC7471 */ + struct te_link_subtlv_av_delay av_delay; + struct te_link_subtlv_mm_delay mm_delay; + struct te_link_subtlv_delay_var delay_var; + struct te_link_subtlv_pkt_loss pkt_loss; + struct te_link_subtlv_res_bw res_bw; + struct te_link_subtlv_ava_bw ava_bw; + struct te_link_subtlv_use_bw use_bw; + + struct in_addr adv_router; + struct in_addr id; +}; + +/* Prototypes. */ +extern int ospf_mpls_te_init(void); +extern void ospf_mpls_te_term(void); +extern void ospf_mpls_te_finish(void); +extern struct ospf_mpls_te *get_ospf_mpls_te(void); +extern void ospf_mpls_te_update_if(struct interface *); +extern void ospf_mpls_te_lsa_schedule(struct mpls_te_link *, enum lsa_opcode); +extern void set_linkparams_llri(struct mpls_te_link *, uint32_t, uint32_t); +extern void set_linkparams_lrrid(struct mpls_te_link *, struct in_addr, + struct in_addr); + +struct zapi_opaque_reg_info; +/** + * Call when a client send a Link State Sync message. In turn, OSPF will send + * the contain of the Link State Data base. + * + * @param info ZAPI Opaque message information + * + * @return 0 on success, -1 otherwise + */ +extern int ospf_te_sync_ted(struct zapi_opaque_reg_info dst); + +#endif /* _ZEBRA_OSPF_MPLS_TE_H */ diff --git a/ospfd/ospf_ti_lfa.c b/ospfd/ospf_ti_lfa.c new file mode 100644 index 0000000..d8a2613 --- /dev/null +++ b/ospfd/ospf_ti_lfa.c @@ -0,0 +1,1108 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF TI-LFA + * Copyright (C) 2020 NetDEF, Inc. + * Sascha Kattelmann + */ + +#include + +#include "prefix.h" +#include "table.h" +#include "printfrr.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_sr.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ti_lfa.h" +#include "ospfd/ospf_dump.h" + + +DECLARE_RBTREE_UNIQ(p_spaces, struct p_space, p_spaces_item, + p_spaces_compare_func); +DECLARE_RBTREE_UNIQ(q_spaces, struct q_space, q_spaces_item, + q_spaces_compare_func); + +static void +ospf_ti_lfa_generate_p_space(struct ospf_area *area, struct vertex *child, + struct protected_resource *protected_resource, + bool recursive, struct list *pc_path); + +void ospf_print_protected_resource( + struct protected_resource *protected_resource, char *buf) +{ + struct router_lsa_link *link; + + switch (protected_resource->type) { + case OSPF_TI_LFA_LINK_PROTECTION: + link = protected_resource->link; + snprintfrr(buf, PROTECTED_RESOURCE_STRLEN, + "protected link: %pI4 %pI4", &link->link_id, + &link->link_data); + break; + case OSPF_TI_LFA_NODE_PROTECTION: + snprintfrr(buf, PROTECTED_RESOURCE_STRLEN, + "protected node: %pI4", + &protected_resource->router_id); + break; + case OSPF_TI_LFA_UNDEFINED_PROTECTION: + snprintfrr(buf, PROTECTED_RESOURCE_STRLEN, + "undefined protected resource"); + break; + } +} + +static enum ospf_ti_lfa_p_q_space_adjacency +ospf_ti_lfa_find_p_node(struct vertex *pc_node, struct p_space *p_space, + struct q_space *q_space) +{ + struct listnode *curr_node; + struct vertex *p_node = NULL, *pc_node_parent, *p_node_pc_parent; + struct vertex_parent *pc_vertex_parent; + + curr_node = listnode_lookup(q_space->pc_path, pc_node); + assert(curr_node); + pc_node_parent = listgetdata(curr_node->next); + + q_space->p_node_info->type = OSPF_TI_LFA_UNDEFINED_NODE; + + p_node = ospf_spf_vertex_find(pc_node_parent->id, p_space->vertex_list); + + if (p_node) { + q_space->p_node_info->node = p_node; + q_space->p_node_info->type = OSPF_TI_LFA_P_NODE; + + if (curr_node->next->next) { + p_node_pc_parent = listgetdata(curr_node->next->next); + pc_vertex_parent = ospf_spf_vertex_parent_find( + p_node_pc_parent->id, pc_node_parent); + q_space->p_node_info->nexthop = + pc_vertex_parent->nexthop->router; + } else { + /* + * It can happen that the P node is the root node itself + * (hence there can be no parents). In this case we + * don't need to set a nexthop. + */ + q_space->p_node_info->nexthop.s_addr = INADDR_ANY; + } + + return OSPF_TI_LFA_P_Q_SPACE_ADJACENT; + } + + ospf_ti_lfa_find_p_node(pc_node_parent, p_space, q_space); + return OSPF_TI_LFA_P_Q_SPACE_NON_ADJACENT; +} + +static void ospf_ti_lfa_find_q_node(struct vertex *pc_node, + struct p_space *p_space, + struct q_space *q_space) +{ + struct listnode *curr_node, *next_node; + struct vertex *p_node, *q_node, *q_space_parent = NULL, *pc_node_parent; + struct vertex_parent *pc_vertex_parent; + + curr_node = listnode_lookup(q_space->pc_path, pc_node); + assert(curr_node); + next_node = curr_node->next; + pc_node_parent = listgetdata(next_node); + pc_vertex_parent = + ospf_spf_vertex_parent_find(pc_node_parent->id, pc_node); + + p_node = ospf_spf_vertex_find(pc_node->id, p_space->vertex_list); + q_node = ospf_spf_vertex_find(pc_node->id, q_space->vertex_list); + + /* The Q node is always present. */ + assert(q_node); + + q_space->q_node_info->type = OSPF_TI_LFA_UNDEFINED_NODE; + + if (p_node && q_node) { + q_space->q_node_info->node = pc_node; + q_space->q_node_info->type = OSPF_TI_LFA_PQ_NODE; + q_space->q_node_info->nexthop = + pc_vertex_parent->nexthop->router; + return; + } + + /* + * Note that the Q space has the 'reverse' direction of the PC + * SPF. Hence compare PC SPF parent to Q space children. + */ + q_space_parent = + ospf_spf_vertex_find(pc_node_parent->id, q_node->children); + + /* + * If the Q space parent doesn't exist we 'hit' the border to the P + * space and hence got our Q node. + */ + if (!q_space_parent) { + q_space->q_node_info->node = pc_node; + q_space->q_node_info->type = OSPF_TI_LFA_Q_NODE; + q_space->q_node_info->nexthop = + pc_vertex_parent->nexthop->router; + return; + } + + return ospf_ti_lfa_find_q_node(pc_node_parent, p_space, q_space); +} + +static void ospf_ti_lfa_append_label_stack(struct mpls_label_stack *label_stack, + mpls_label_t labels[], + uint32_t num_labels) +{ + int i, offset, limit; + + limit = label_stack->num_labels + num_labels; + offset = label_stack->num_labels; + + for (i = label_stack->num_labels; i < limit; i++) { + label_stack->label[i] = labels[i - offset]; + label_stack->num_labels++; + } +} + + +static struct mpls_label_stack * +ospf_ti_lfa_create_label_stack(mpls_label_t labels[], uint32_t num_labels) +{ + struct mpls_label_stack *label_stack; + uint32_t i; + + /* Sanity check */ + for (i = 0; i < num_labels; i++) { + if (labels[i] == MPLS_INVALID_LABEL) + return NULL; + } + + label_stack = XCALLOC(MTYPE_OSPF_Q_SPACE, + sizeof(struct mpls_label_stack) + + MPLS_MAX_LABELS * sizeof(mpls_label_t)); + label_stack->num_labels = num_labels; + + for (i = 0; i < num_labels; i++) + label_stack->label[i] = labels[i]; + + return label_stack; +} + +static struct list * +ospf_ti_lfa_map_path_to_pc_vertices(struct list *path, + struct list *pc_vertex_list) +{ + struct listnode *node; + struct vertex *vertex, *pc_vertex; + struct list *pc_path; + + pc_path = list_new(); + + for (ALL_LIST_ELEMENTS_RO(path, node, vertex)) { + pc_vertex = ospf_spf_vertex_find(vertex->id, pc_vertex_list); + listnode_add(pc_path, pc_vertex); + } + + return pc_path; +} + +static struct list *ospf_ti_lfa_cut_out_pc_path(struct list *pc_vertex_list, + struct list *pc_path, + struct vertex *p_node, + struct vertex *q_node) +{ + struct list *inner_pc_path; + struct vertex *current_vertex; + struct listnode *current_listnode; + + inner_pc_path = list_new(); + current_vertex = ospf_spf_vertex_find(q_node->id, pc_vertex_list); + current_listnode = listnode_lookup(pc_path, current_vertex); + + /* Note that the post-convergence paths are reversed. */ + while (current_listnode) { + current_vertex = listgetdata(current_listnode); + listnode_add(inner_pc_path, current_vertex); + + if (current_vertex->id.s_addr == p_node->id.s_addr) + break; + + current_listnode = current_listnode->next; + } + + return inner_pc_path; +} + +static void ospf_ti_lfa_generate_inner_label_stack( + struct ospf_area *area, struct p_space *p_space, + struct q_space *q_space, + struct ospf_ti_lfa_inner_backup_path_info *inner_backup_path_info) +{ + struct route_table *new_table; + struct vertex *q_node; + struct vertex *start_vertex, *end_vertex; + struct vertex_parent *vertex_parent; + struct listnode *pc_p_node, *pc_q_node; + struct vertex *spf_orig; + struct list *vertex_list_orig; + struct p_spaces_head *p_spaces_orig; + struct p_space *inner_p_space; + struct q_space *inner_q_space; + struct ospf_ti_lfa_node_info *p_node_info, *q_node_info; + struct protected_resource *protected_resource; + struct list *inner_pc_path; + mpls_label_t start_label, end_label; + + p_node_info = q_space->p_node_info; + q_node_info = q_space->q_node_info; + protected_resource = p_space->protected_resource; + + start_vertex = p_node_info->node; + end_vertex = q_node_info->node; + + /* + * It can happen that the P node and/or the Q node are the root or + * the destination, therefore we need to force one step forward (resp. + * backward) using an Adjacency-SID. + */ + start_label = MPLS_INVALID_LABEL; + end_label = MPLS_INVALID_LABEL; + if (p_node_info->node->id.s_addr == p_space->root->id.s_addr) { + pc_p_node = listnode_lookup(q_space->pc_path, p_space->pc_spf); + assert(pc_p_node); + start_vertex = listgetdata(pc_p_node->prev); + start_label = ospf_sr_get_adj_sid_by_id(&p_node_info->node->id, + &start_vertex->id); + } + if (q_node_info->node->id.s_addr == q_space->root->id.s_addr) { + pc_q_node = listnode_lookup(q_space->pc_path, + listnode_head(q_space->pc_path)); + assert(pc_q_node); + end_vertex = listgetdata(pc_q_node->next); + end_label = ospf_sr_get_adj_sid_by_id(&end_vertex->id, + &q_node_info->node->id); + } + + /* Corner case: inner path is just one node */ + if (start_vertex->id.s_addr == end_vertex->id.s_addr) { + inner_backup_path_info->label_stack = + ospf_ti_lfa_create_label_stack(&start_label, 1); + inner_backup_path_info->q_node_info.node = end_vertex; + inner_backup_path_info->q_node_info.type = OSPF_TI_LFA_PQ_NODE; + inner_backup_path_info->p_node_info.type = + OSPF_TI_LFA_UNDEFINED_NODE; + vertex_parent = ospf_spf_vertex_parent_find(p_space->root->id, + end_vertex); + inner_backup_path_info->p_node_info.nexthop = + vertex_parent->nexthop->router; + return; + } + + inner_pc_path = ospf_ti_lfa_cut_out_pc_path(p_space->pc_vertex_list, + q_space->pc_path, + start_vertex, end_vertex); + + new_table = route_table_init(); + + /* Copy the current state ... */ + spf_orig = area->spf; + vertex_list_orig = area->spf_vertex_list; + p_spaces_orig = area->p_spaces; + + area->p_spaces = + XCALLOC(MTYPE_OSPF_P_SPACE, sizeof(struct p_spaces_head)); + + /* dry run true, root node false */ + ospf_spf_calculate(area, start_vertex->lsa_p, new_table, NULL, NULL, + true, false); + + q_node = ospf_spf_vertex_find(end_vertex->id, area->spf_vertex_list); + + ospf_ti_lfa_generate_p_space(area, q_node, protected_resource, false, + inner_pc_path); + + /* There's just one P and Q space */ + inner_p_space = p_spaces_pop(area->p_spaces); + inner_q_space = q_spaces_pop(inner_p_space->q_spaces); + + /* Copy over inner backup path information from the inner q_space */ + + /* In case the outer P node is also the root of the P space */ + if (start_label != MPLS_INVALID_LABEL) { + inner_backup_path_info->label_stack = + ospf_ti_lfa_create_label_stack(&start_label, 1); + ospf_ti_lfa_append_label_stack( + inner_backup_path_info->label_stack, + inner_q_space->label_stack->label, + inner_q_space->label_stack->num_labels); + inner_backup_path_info->p_node_info.node = start_vertex; + inner_backup_path_info->p_node_info.type = OSPF_TI_LFA_P_NODE; + vertex_parent = ospf_spf_vertex_parent_find(p_space->root->id, + start_vertex); + inner_backup_path_info->p_node_info.nexthop = + vertex_parent->nexthop->router; + } else { + memcpy(inner_backup_path_info->label_stack, + inner_q_space->label_stack, + sizeof(struct mpls_label_stack) + + sizeof(mpls_label_t) + * inner_q_space->label_stack + ->num_labels); + memcpy(&inner_backup_path_info->p_node_info, + inner_q_space->p_node_info, + sizeof(struct ospf_ti_lfa_node_info)); + } + + /* In case the outer Q node is also the root of the Q space */ + if (end_label != MPLS_INVALID_LABEL) { + inner_backup_path_info->q_node_info.node = end_vertex; + inner_backup_path_info->q_node_info.type = OSPF_TI_LFA_Q_NODE; + } else { + memcpy(&inner_backup_path_info->q_node_info, + inner_q_space->q_node_info, + sizeof(struct ospf_ti_lfa_node_info)); + } + + /* Cleanup */ + ospf_ti_lfa_free_p_spaces(area); + ospf_spf_cleanup(area->spf, area->spf_vertex_list); + + /* ... and copy the current state back. */ + area->spf = spf_orig; + area->spf_vertex_list = vertex_list_orig; + area->p_spaces = p_spaces_orig; +} + +static void ospf_ti_lfa_generate_label_stack(struct ospf_area *area, + struct p_space *p_space, + struct q_space *q_space) +{ + enum ospf_ti_lfa_p_q_space_adjacency adjacency_result; + mpls_label_t labels[MPLS_MAX_LABELS]; + struct vertex *pc_node; + struct ospf_ti_lfa_inner_backup_path_info inner_backup_path_info; + + if (IS_DEBUG_OSPF_TI_LFA) + zlog_debug( + "%s: Generating Label stack for src %pI4 and dest %pI4.", + __func__, &p_space->root->id, &q_space->root->id); + + pc_node = listnode_head(q_space->pc_path); + + if (!pc_node) { + if (IS_DEBUG_OSPF_TI_LFA) + zlog_debug( + "%s: There seems to be no post convergence path (yet).", + __func__); + return; + } + + ospf_ti_lfa_find_q_node(pc_node, p_space, q_space); + if (q_space->q_node_info->type == OSPF_TI_LFA_UNDEFINED_NODE) { + if (IS_DEBUG_OSPF_TI_LFA) + zlog_debug("%s: Q node not found!", __func__); + return; + } + + /* Found a PQ node? Then we are done here. */ + if (q_space->q_node_info->type == OSPF_TI_LFA_PQ_NODE) { + /* + * If the PQ node is a child of the root, then we can use an + * adjacency SID instead of a prefix SID for the backup path. + */ + if (ospf_spf_vertex_parent_find(p_space->root->id, + q_space->q_node_info->node)) + labels[0] = ospf_sr_get_adj_sid_by_id( + &p_space->root->id, + &q_space->q_node_info->node->id); + else + labels[0] = ospf_sr_get_prefix_sid_by_id( + &q_space->q_node_info->node->id); + + q_space->label_stack = + ospf_ti_lfa_create_label_stack(labels, 1); + q_space->nexthop = q_space->q_node_info->nexthop; + + return; + } + + /* Otherwise find a (hopefully adjacent) P node. */ + pc_node = ospf_spf_vertex_find(q_space->q_node_info->node->id, + p_space->pc_vertex_list); + adjacency_result = ospf_ti_lfa_find_p_node(pc_node, p_space, q_space); + + if (q_space->p_node_info->type == OSPF_TI_LFA_UNDEFINED_NODE) { + if (IS_DEBUG_OSPF_TI_LFA) + zlog_debug("%s: P node not found!", __func__); + return; + } + + /* + * This should be the regular case: P and Q space are adjacent or even + * overlapping. This is guaranteed for link protection when used with + * symmetric weights. + */ + if (adjacency_result == OSPF_TI_LFA_P_Q_SPACE_ADJACENT) { + /* + * It can happen that the P node is the root itself, therefore + * we don't need a label for it. So just one adjacency SID for + * the Q node. + */ + if (q_space->p_node_info->node->id.s_addr + == p_space->root->id.s_addr) { + labels[0] = ospf_sr_get_adj_sid_by_id( + &p_space->root->id, + &q_space->q_node_info->node->id); + q_space->label_stack = + ospf_ti_lfa_create_label_stack(labels, 1); + q_space->nexthop = q_space->q_node_info->nexthop; + return; + } + + /* + * Otherwise we have a P and also a Q node (which are adjacent). + * + * It can happen that the P node is a child of the root, + * therefore we might just need the adjacency SID for the P node + * instead of the prefix SID. For the Q node always take the + * adjacency SID. + */ + if (ospf_spf_vertex_parent_find(p_space->root->id, + q_space->p_node_info->node)) + labels[0] = ospf_sr_get_adj_sid_by_id( + &p_space->root->id, + &q_space->p_node_info->node->id); + else + labels[0] = ospf_sr_get_prefix_sid_by_id( + &q_space->p_node_info->node->id); + + labels[1] = ospf_sr_get_adj_sid_by_id( + &q_space->p_node_info->node->id, + &q_space->q_node_info->node->id); + + q_space->label_stack = + ospf_ti_lfa_create_label_stack(labels, 2); + q_space->nexthop = q_space->p_node_info->nexthop; + + } else { + /* + * It can happen that the P and Q space are not adjacent when + * e.g. node protection or asymmetric weights are used. In this + * case the found P and Q nodes are used as a reference for + * another run of the algorithm! + * + * After having found the inner label stack it is stitched + * together with the outer labels. + */ + inner_backup_path_info.label_stack = XCALLOC( + MTYPE_OSPF_PATH, + sizeof(struct mpls_label_stack) + + sizeof(mpls_label_t) * MPLS_MAX_LABELS); + ospf_ti_lfa_generate_inner_label_stack(area, p_space, q_space, + &inner_backup_path_info); + + /* + * First stitch together the outer P node label with the inner + * label stack. + */ + if (q_space->p_node_info->node->id.s_addr + == p_space->root->id.s_addr) { + /* + * It can happen that the P node is the root itself, + * therefore we don't need a label for it. Just take + * the inner label stack first. + */ + q_space->label_stack = ospf_ti_lfa_create_label_stack( + inner_backup_path_info.label_stack->label, + inner_backup_path_info.label_stack->num_labels); + + /* Use the inner P or Q node for the nexthop */ + if (inner_backup_path_info.p_node_info.type + != OSPF_TI_LFA_UNDEFINED_NODE) + q_space->nexthop = inner_backup_path_info + .p_node_info.nexthop; + else + q_space->nexthop = inner_backup_path_info + .q_node_info.nexthop; + + } else if (ospf_spf_vertex_parent_find( + p_space->root->id, + q_space->p_node_info->node)) { + /* + * It can happen that the outer P node is a child of + * the root, therefore we might just need the + * adjacency SID for the outer P node instead of the + * prefix SID. Then just append the inner label stack. + */ + labels[0] = ospf_sr_get_adj_sid_by_id( + &p_space->root->id, + &q_space->p_node_info->node->id); + q_space->label_stack = + ospf_ti_lfa_create_label_stack(labels, 1); + ospf_ti_lfa_append_label_stack( + q_space->label_stack, + inner_backup_path_info.label_stack->label, + inner_backup_path_info.label_stack->num_labels); + q_space->nexthop = q_space->p_node_info->nexthop; + } else { + /* The outer P node needs a Prefix-SID here */ + labels[0] = ospf_sr_get_prefix_sid_by_id( + &q_space->p_node_info->node->id); + q_space->label_stack = + ospf_ti_lfa_create_label_stack(labels, 1); + ospf_ti_lfa_append_label_stack( + q_space->label_stack, + inner_backup_path_info.label_stack->label, + inner_backup_path_info.label_stack->num_labels); + q_space->nexthop = q_space->p_node_info->nexthop; + } + + /* Now the outer Q node needs to be considered */ + if (ospf_spf_vertex_parent_find( + inner_backup_path_info.q_node_info.node->id, + q_space->q_node_info->node)) { + /* + * The outer Q node can be a child of the inner Q node, + * hence just add an Adjacency-SID. + */ + labels[0] = ospf_sr_get_adj_sid_by_id( + &inner_backup_path_info.q_node_info.node->id, + &q_space->q_node_info->node->id); + ospf_ti_lfa_append_label_stack(q_space->label_stack, + labels, 1); + } else { + /* Otherwise a Prefix-SID is needed */ + labels[0] = ospf_sr_get_prefix_sid_by_id( + &q_space->q_node_info->node->id); + ospf_ti_lfa_append_label_stack(q_space->label_stack, + labels, 1); + } + /* + * Note that there's also the case where the inner and outer Q + * node are the same, but then there's nothing to do! + */ + } +} + +static struct list * +ospf_ti_lfa_generate_post_convergence_path(struct list *pc_vertex_list, + struct vertex *dest) +{ + struct list *pc_path; + struct vertex *current_vertex; + struct vertex_parent *parent; + + current_vertex = ospf_spf_vertex_find(dest->id, pc_vertex_list); + if (!current_vertex) { + if (IS_DEBUG_OSPF_TI_LFA) + zlog_debug( + "%s: There seems to be no post convergence path (yet).", + __func__); + return NULL; + } + + pc_path = list_new(); + listnode_add(pc_path, current_vertex); + + /* Generate a backup path in reverse order */ + for (;;) { + parent = listnode_head(current_vertex->parents); + if (!parent) + break; + + listnode_add(pc_path, parent->parent); + current_vertex = parent->parent; + } + + return pc_path; +} + +static void ospf_ti_lfa_generate_q_spaces(struct ospf_area *area, + struct p_space *p_space, + struct vertex *dest, bool recursive, + struct list *pc_path) +{ + struct listnode *node; + struct vertex *child; + struct route_table *new_table; + struct q_space *q_space, q_space_search; + char label_buf[MPLS_LABEL_STRLEN]; + char res_buf[PROTECTED_RESOURCE_STRLEN]; + bool node_protected; + + ospf_print_protected_resource(p_space->protected_resource, res_buf); + node_protected = + p_space->protected_resource->type == OSPF_TI_LFA_NODE_PROTECTION + && dest->id.s_addr + == p_space->protected_resource->router_id.s_addr; + + /* + * If node protection is used, don't build a Q space for the protected + * node of that particular P space. Move on with children instead. + */ + if (node_protected) { + if (recursive) { + /* Recursively generate Q spaces for all children */ + for (ALL_LIST_ELEMENTS_RO(dest->children, node, child)) + ospf_ti_lfa_generate_q_spaces(area, p_space, + child, recursive, + pc_path); + } + return; + } + + /* Check if we already have a Q space for this destination */ + q_space_search.root = dest; + if (q_spaces_find(p_space->q_spaces, &q_space_search)) + return; + + q_space = XCALLOC(MTYPE_OSPF_Q_SPACE, sizeof(struct q_space)); + q_space->p_node_info = XCALLOC(MTYPE_OSPF_Q_SPACE, + sizeof(struct ospf_ti_lfa_node_info)); + q_space->q_node_info = XCALLOC(MTYPE_OSPF_Q_SPACE, + sizeof(struct ospf_ti_lfa_node_info)); + + new_table = route_table_init(); + + /* + * Generate a new (reversed!) SPF tree for this vertex, + * dry run true, root node false + */ + area->spf_reversed = true; + ospf_spf_calculate(area, dest->lsa_p, new_table, NULL, NULL, true, + false); + + /* Reset the flag for reverse SPF */ + area->spf_reversed = false; + + q_space->root = area->spf; + q_space->vertex_list = area->spf_vertex_list; + q_space->label_stack = NULL; + + if (pc_path) + q_space->pc_path = ospf_ti_lfa_map_path_to_pc_vertices( + pc_path, p_space->pc_vertex_list); + else + q_space->pc_path = ospf_ti_lfa_generate_post_convergence_path( + p_space->pc_vertex_list, q_space->root); + + /* If there's no backup path available then we are done here. */ + if (!q_space->pc_path) { + zlog_info( + "%s: NO backup path found for root %pI4 and destination %pI4 for %s, aborting ...", + __func__, &p_space->root->id, &q_space->root->id, + res_buf); + + list_delete(&q_space->vertex_list); + XFREE(MTYPE_OSPF_Q_SPACE, q_space->p_node_info); + XFREE(MTYPE_OSPF_Q_SPACE, q_space->q_node_info); + XFREE(MTYPE_OSPF_Q_SPACE, q_space); + + return; + } + + /* 'Cut' the protected resource out of the new SPF tree */ + ospf_spf_remove_resource(q_space->root, q_space->vertex_list, + p_space->protected_resource); + + /* + * Generate the smallest possible label stack from the root of the P + * space to the root of the Q space. + */ + ospf_ti_lfa_generate_label_stack(area, p_space, q_space); + + if (q_space->label_stack) { + mpls_label2str(q_space->label_stack->num_labels, + q_space->label_stack->label, label_buf, + MPLS_LABEL_STRLEN, 0, true); + zlog_info( + "%s: Generated label stack %s for root %pI4 and destination %pI4 for %s", + __func__, label_buf, &p_space->root->id, + &q_space->root->id, res_buf); + } else { + zlog_info( + "%s: NO label stack generated for root %pI4 and destination %pI4 for %s", + __func__, &p_space->root->id, &q_space->root->id, + res_buf); + } + + /* We are finished, store the new Q space in the P space struct */ + q_spaces_add(p_space->q_spaces, q_space); + + /* Recursively generate Q spaces for all children */ + if (recursive) { + for (ALL_LIST_ELEMENTS_RO(dest->children, node, child)) + ospf_ti_lfa_generate_q_spaces(area, p_space, child, + recursive, pc_path); + } +} + +static void ospf_ti_lfa_generate_post_convergence_spf(struct ospf_area *area, + struct p_space *p_space) +{ + struct route_table *new_table; + + new_table = route_table_init(); + + area->spf_protected_resource = p_space->protected_resource; + + /* + * The 'post convergence' SPF tree is generated here + * dry run true, root node false + * + * So how does this work? During the SPF calculation the algorithm + * checks if a link belongs to a protected resource and then just + * ignores it. + * This is actually _NOT_ a good way to calculate the post + * convergence SPF tree. The preferred way would be to delete the + * relevant links (and nodes) from a copy of the LSDB and then just run + * the SPF algorithm on that as usual. + * However, removing links from router LSAs appears to be its own + * endeavour (because LSAs are stored as a 'raw' stream), so we go with + * this rather hacky way for now. + */ + ospf_spf_calculate(area, area->router_lsa_self, new_table, NULL, NULL, + true, false); + + p_space->pc_spf = area->spf; + p_space->pc_vertex_list = area->spf_vertex_list; + + area->spf_protected_resource = NULL; +} + +static void +ospf_ti_lfa_generate_p_space(struct ospf_area *area, struct vertex *child, + struct protected_resource *protected_resource, + bool recursive, struct list *pc_path) +{ + struct vertex *spf_orig; + struct list *vertex_list, *vertex_list_orig; + struct p_space *p_space; + + p_space = XCALLOC(MTYPE_OSPF_P_SPACE, sizeof(struct p_space)); + vertex_list = list_new(); + + /* The P-space will get its own SPF tree, so copy the old one */ + ospf_spf_copy(area->spf, vertex_list); + p_space->root = listnode_head(vertex_list); + p_space->vertex_list = vertex_list; + p_space->protected_resource = protected_resource; + + /* Initialize the Q spaces for this P space and protected resource */ + p_space->q_spaces = + XCALLOC(MTYPE_OSPF_Q_SPACE, sizeof(struct q_spaces_head)); + q_spaces_init(p_space->q_spaces); + + /* 'Cut' the protected resource out of the new SPF tree */ + ospf_spf_remove_resource(p_space->root, p_space->vertex_list, + p_space->protected_resource); + + /* + * Since we are going to calculate more SPF trees for Q spaces, keep the + * 'original' one here temporarily + */ + spf_orig = area->spf; + vertex_list_orig = area->spf_vertex_list; + + /* Generate the post convergence SPF as a blueprint for backup paths */ + ospf_ti_lfa_generate_post_convergence_spf(area, p_space); + + /* Generate the relevant Q spaces for this particular P space */ + ospf_ti_lfa_generate_q_spaces(area, p_space, child, recursive, pc_path); + + /* Put the 'original' SPF tree back in place */ + area->spf = spf_orig; + area->spf_vertex_list = vertex_list_orig; + + /* We are finished, store the new P space */ + p_spaces_add(area->p_spaces, p_space); +} + +void ospf_ti_lfa_generate_p_spaces(struct ospf_area *area, + enum protection_type protection_type) +{ + struct listnode *node, *inner_node; + struct vertex *root, *child; + struct vertex_parent *vertex_parent; + uint8_t *p, *lim; + struct router_lsa_link *l = NULL; + struct prefix stub_prefix, child_prefix; + struct protected_resource *protected_resource; + + area->p_spaces = + XCALLOC(MTYPE_OSPF_P_SPACE, sizeof(struct p_spaces_head)); + p_spaces_init(area->p_spaces); + + root = area->spf; + + /* Root or its router LSA was not created yet? */ + if (!root || !root->lsa) + return; + + stub_prefix.family = AF_INET; + child_prefix.family = AF_INET; + child_prefix.prefixlen = IPV4_MAX_BITLEN; + + p = ((uint8_t *)root->lsa) + OSPF_LSA_HEADER_SIZE + 4; + lim = ((uint8_t *)root->lsa) + ntohs(root->lsa->length); + + zlog_info("%s: Generating P spaces for area %pI4", __func__, + &area->area_id); + + /* + * Iterate over all stub networks which target other OSPF neighbors. + * Check the nexthop of the child vertex if a stub network is relevant. + */ + while (p < lim) { + l = (struct router_lsa_link *)p; + p += (OSPF_ROUTER_LSA_LINK_SIZE + + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); + + /* First comes node protection */ + if (protection_type == OSPF_TI_LFA_NODE_PROTECTION) { + if (l->m[0].type == LSA_LINK_TYPE_POINTOPOINT) { + protected_resource = XCALLOC( + MTYPE_OSPF_P_SPACE, + sizeof(struct protected_resource)); + protected_resource->type = protection_type; + protected_resource->router_id = l->link_id; + child = ospf_spf_vertex_find( + protected_resource->router_id, + root->children); + if (child) + ospf_ti_lfa_generate_p_space( + area, child, protected_resource, + true, NULL); + } + + continue; + } + + /* The rest is about link protection */ + if (protection_type != OSPF_TI_LFA_LINK_PROTECTION) + continue; + + if (l->m[0].type != LSA_LINK_TYPE_STUB) + continue; + + stub_prefix.prefixlen = ip_masklen(l->link_data); + stub_prefix.u.prefix4 = l->link_id; + + for (ALL_LIST_ELEMENTS_RO(root->children, node, child)) { + + if (child->type != OSPF_VERTEX_ROUTER) + continue; + + for (ALL_LIST_ELEMENTS_RO(child->parents, inner_node, + vertex_parent)) { + + child_prefix.u.prefix4 = + vertex_parent->nexthop->router; + + /* + * If there's a link for that stub network then + * we will protect it. Hence generate a P space + * for that particular link including the + * Q spaces so we can later on generate a + * backup path for the link. + */ + if (prefix_match(&stub_prefix, &child_prefix)) { + zlog_info( + "%s: Generating P space for %pI4", + __func__, &l->link_id); + + protected_resource = XCALLOC( + MTYPE_OSPF_P_SPACE, + sizeof(struct + protected_resource)); + protected_resource->type = + protection_type; + protected_resource->link = l; + + ospf_ti_lfa_generate_p_space( + area, child, protected_resource, + true, NULL); + } + } + } + } +} + +static struct p_space *ospf_ti_lfa_get_p_space_by_path(struct ospf_area *area, + struct ospf_path *path) +{ + struct p_space *p_space; + struct router_lsa_link *link; + struct vertex *child; + int type; + + frr_each(p_spaces, area->p_spaces, p_space) { + type = p_space->protected_resource->type; + + if (type == OSPF_TI_LFA_LINK_PROTECTION) { + link = p_space->protected_resource->link; + if ((path->nexthop.s_addr & link->link_data.s_addr) + == (link->link_id.s_addr & link->link_data.s_addr)) + return p_space; + } + + if (type == OSPF_TI_LFA_NODE_PROTECTION) { + child = ospf_spf_vertex_by_nexthop(area->spf, + &path->nexthop); + if (child + && p_space->protected_resource->router_id.s_addr + == child->id.s_addr) + return p_space; + } + } + + return NULL; +} + +void ospf_ti_lfa_insert_backup_paths(struct ospf_area *area, + struct route_table *new_table) +{ + struct route_node *rn; + struct ospf_route *or; + struct ospf_path *path; + struct listnode *node; + struct p_space *p_space; + struct q_space *q_space, q_space_search; + struct vertex root_search; + char label_buf[MPLS_LABEL_STRLEN]; + + for (rn = route_top(new_table); rn; rn = route_next(rn)) { + or = rn->info; + if (or == NULL) + continue; + + /* Insert a backup path for all OSPF paths */ + for (ALL_LIST_ELEMENTS_RO(or->paths, node, path)) { + + if (path->adv_router.s_addr == INADDR_ANY + || path->nexthop.s_addr == INADDR_ANY) + continue; + + if (IS_DEBUG_OSPF_TI_LFA) + zlog_debug( + "%s: attempting to insert backup path for prefix %pFX, router id %pI4 and nexthop %pI4.", + __func__, &rn->p, &path->adv_router, + &path->nexthop); + + p_space = ospf_ti_lfa_get_p_space_by_path(area, path); + if (!p_space) { + if (IS_DEBUG_OSPF_TI_LFA) + zlog_debug( + "%s: P space not found for router id %pI4 and nexthop %pI4.", + __func__, &path->adv_router, + &path->nexthop); + continue; + } + + root_search.id = path->adv_router; + q_space_search.root = &root_search; + q_space = q_spaces_find(p_space->q_spaces, + &q_space_search); + if (!q_space) { + if (IS_DEBUG_OSPF_TI_LFA) + zlog_debug( + "%s: Q space not found for advertising router %pI4.", + __func__, &path->adv_router); + continue; + } + + /* If there's a backup label stack, insert it*/ + if (q_space->label_stack) { + /* Init the backup path data in path */ + path->srni.backup_label_stack = XCALLOC( + MTYPE_OSPF_PATH, + sizeof(struct mpls_label_stack) + + sizeof(mpls_label_t) + * q_space->label_stack + ->num_labels); + + /* Copy over the label stack */ + path->srni.backup_label_stack->num_labels = + q_space->label_stack->num_labels; + memcpy(path->srni.backup_label_stack->label, + q_space->label_stack->label, + sizeof(mpls_label_t) + * q_space->label_stack + ->num_labels); + + /* Set the backup nexthop too */ + path->srni.backup_nexthop = q_space->nexthop; + } + + if (path->srni.backup_label_stack) { + mpls_label2str( + path->srni.backup_label_stack + ->num_labels, + path->srni.backup_label_stack->label, + label_buf, MPLS_LABEL_STRLEN, 0, true); + if (IS_DEBUG_OSPF_TI_LFA) + zlog_debug( + "%s: inserted backup path %s for prefix %pFX, router id %pI4 and nexthop %pI4.", + __func__, label_buf, &rn->p, + &path->adv_router, + &path->nexthop); + } else { + if (IS_DEBUG_OSPF_TI_LFA) + zlog_debug( + "%s: inserted NO backup path for prefix %pFX, router id %pI4 and nexthop %pI4.", + __func__, &rn->p, + &path->adv_router, + &path->nexthop); + } + } + } +} + +void ospf_ti_lfa_free_p_spaces(struct ospf_area *area) +{ + struct p_space *p_space; + struct q_space *q_space; + + while ((p_space = p_spaces_pop(area->p_spaces))) { + while ((q_space = q_spaces_pop(p_space->q_spaces))) { + ospf_spf_cleanup(q_space->root, q_space->vertex_list); + + if (q_space->pc_path) + list_delete(&q_space->pc_path); + + XFREE(MTYPE_OSPF_Q_SPACE, q_space->p_node_info); + XFREE(MTYPE_OSPF_Q_SPACE, q_space->q_node_info); + XFREE(MTYPE_OSPF_Q_SPACE, q_space->label_stack); + XFREE(MTYPE_OSPF_Q_SPACE, q_space); + } + + ospf_spf_cleanup(p_space->root, p_space->vertex_list); + ospf_spf_cleanup(p_space->pc_spf, p_space->pc_vertex_list); + XFREE(MTYPE_OSPF_P_SPACE, p_space->protected_resource); + + q_spaces_fini(p_space->q_spaces); + XFREE(MTYPE_OSPF_Q_SPACE, p_space->q_spaces); + XFREE(MTYPE_OSPF_P_SPACE, p_space); + } + + p_spaces_fini(area->p_spaces); + XFREE(MTYPE_OSPF_P_SPACE, area->p_spaces); +} + +void ospf_ti_lfa_compute(struct ospf_area *area, struct route_table *new_table, + enum protection_type protection_type) +{ + /* + * Generate P spaces per protected link/node and their respective Q + * spaces, generate backup paths (MPLS label stacks) by finding P/Q + * nodes. + */ + ospf_ti_lfa_generate_p_spaces(area, protection_type); + + /* Insert the generated backup paths into the routing table. */ + ospf_ti_lfa_insert_backup_paths(area, new_table); + + /* Cleanup P spaces and related datastructures including Q spaces. */ + ospf_ti_lfa_free_p_spaces(area); +} diff --git a/ospfd/ospf_ti_lfa.h b/ospfd/ospf_ti_lfa.h new file mode 100644 index 0000000..ea37da6 --- /dev/null +++ b/ospfd/ospf_ti_lfa.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF calculation. + * Copyright (C) 2020 NetDEF, Inc. + * Sascha Kattelmann + */ + +#ifndef _OSPF_TI_LFA_H +#define _OSPF_TI_LFA_H + +#define PROTECTED_RESOURCE_STRLEN 100 + +extern void ospf_ti_lfa_compute(struct ospf_area *area, + struct route_table *new_table, + enum protection_type protection_type); + +/* unit testing */ +extern void ospf_ti_lfa_generate_p_spaces(struct ospf_area *area, + enum protection_type protection_type); +extern void ospf_ti_lfa_insert_backup_paths(struct ospf_area *area, + struct route_table *new_table); +extern void ospf_ti_lfa_free_p_spaces(struct ospf_area *area); +void ospf_print_protected_resource( + struct protected_resource *protected_resource, char *buf); + +#endif /* _OSPF_TI_LFA_H */ diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c new file mode 100644 index 0000000..355742a --- /dev/null +++ b/ospfd/ospf_vty.c @@ -0,0 +1,13798 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* OSPF VTY interface. + * Copyright (C) 2005 6WIND + * Copyright (C) 2000 Toshiaki Takada + */ + +#include +#include + +#include "printfrr.h" +#include "monotime.h" +#include "memory.h" +#include "frrevent.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "command.h" +#include "plist.h" +#include "log.h" +#include "zclient.h" +#include +#include "defaults.h" +#include "lib/printfrr.h" +#include "keychain.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_vty.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_bfd.h" +#include "ospfd/ospf_ldp_sync.h" +#include "ospfd/ospf_network.h" +#include "ospfd/ospf_memory.h" + +FRR_CFG_DEFAULT_BOOL(OSPF_LOG_ADJACENCY_CHANGES, + { .val_bool = true, .match_profile = "datacenter", }, + { .val_bool = false }, +); + +static const char *const ospf_network_type_str[] = { + "Null", "POINTOPOINT", "BROADCAST", "NBMA", "POINTOMULTIPOINT", + "VIRTUALLINK", "LOOPBACK"}; + +/* 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 ? OSPF_AREA_ID_FMT_DOTTEDQUAD : OSPF_AREA_ID_FMT_DECIMAL; + + return 0; +} + +static void area_id2str(char *buf, int length, struct in_addr *area_id, + int area_id_fmt) +{ + if (area_id_fmt == OSPF_AREA_ID_FMT_DOTTEDQUAD) + inet_ntop(AF_INET, area_id, buf, length); + else + snprintf(buf, length, "%lu", + (unsigned long)ntohl(area_id->s_addr)); +} + +static int str2metric(const char *str, int *metric) +{ + /* Sanity check. */ + if (str == NULL) + return 0; + + *metric = strtol(str, NULL, 10); + if (*metric < 0 || *metric > 16777214) { + /* vty_out (vty, "OSPF metric value is invalid\n"); */ + return 0; + } + + return 1; +} + +static int str2metric_type(const char *str, int *metric_type) +{ + /* Sanity check. */ + if (str == NULL) + return 0; + + if (strncmp(str, "1", 1) == 0) + *metric_type = EXTERNAL_METRIC_TYPE_1; + else if (strncmp(str, "2", 1) == 0) + *metric_type = EXTERNAL_METRIC_TYPE_2; + else + return 0; + + return 1; +} + +int ospf_oi_count(struct interface *ifp) +{ + struct route_node *rn; + int i = 0; + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) + if (rn->info) + i++; + + return i; +} + +#define OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf) \ + if (argv_find(argv, argc, "vrf", &idx_vrf)) { \ + vrf_name = argv[idx_vrf + 1]->arg; \ + all_vrf = strmatch(vrf_name, "all"); \ + } + +static int ospf_router_cmd_parse(struct vty *vty, struct cmd_token *argv[], + const int argc, unsigned short *instance, + const char **vrf_name) +{ + int idx_vrf = 0, idx_inst = 0; + + *instance = 0; + if (argv_find(argv, argc, "(1-65535)", &idx_inst)) { + if (ospf_instance == 0) { + vty_out(vty, + "%% OSPF is not running in instance mode\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + *instance = strtoul(argv[idx_inst]->arg, NULL, 10); + } + + *vrf_name = VRF_DEFAULT_NAME; + if (argv_find(argv, argc, "vrf", &idx_vrf)) { + if (ospf_instance != 0) { + vty_out(vty, + "%% VRF is not supported in instance mode\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + *vrf_name = argv[idx_vrf + 1]->arg; + } + + return CMD_SUCCESS; +} + +static void ospf_show_vrf_name(struct ospf *ospf, struct vty *vty, + json_object *json, uint8_t use_vrf) +{ + if (use_vrf) { + if (json) { + json_object_string_add(json, "vrfName", + ospf_get_name(ospf)); + json_object_int_add(json, "vrfId", ospf->vrf_id); + } else + vty_out(vty, "VRF Name: %s\n", ospf_get_name(ospf)); + } +} + +#include "ospfd/ospf_vty_clippy.c" + +DEFUN_NOSH (router_ospf, + router_ospf_cmd, + "router ospf [{(1-65535)|vrf NAME}]", + "Enable a routing process\n" + "Start OSPF configuration\n" + "Instance ID\n" + VRF_CMD_HELP_STR) +{ + unsigned short instance; + const char *vrf_name; + bool created = false; + struct ospf *ospf; + int ret; + + ret = ospf_router_cmd_parse(vty, argv, argc, &instance, &vrf_name); + if (ret != CMD_SUCCESS) + return ret; + + if (instance != ospf_instance) { + VTY_PUSH_CONTEXT_NULL(OSPF_NODE); + return CMD_NOT_MY_INSTANCE; + } + + ospf = ospf_get(instance, vrf_name, &created); + + if (created) + if (DFLT_OSPF_LOG_ADJACENCY_CHANGES) + SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES); + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Config command 'router ospf %d' received, vrf %s id %u oi_running %u", + ospf->instance, ospf_get_name(ospf), ospf->vrf_id, + ospf->oi_running); + + VTY_PUSH_CONTEXT(OSPF_NODE, ospf); + + return ret; +} + +DEFUN (no_router_ospf, + no_router_ospf_cmd, + "no router ospf [{(1-65535)|vrf NAME}]", + NO_STR + "Enable a routing process\n" + "Start OSPF configuration\n" + "Instance ID\n" + VRF_CMD_HELP_STR) +{ + unsigned short instance; + const char *vrf_name; + struct ospf *ospf; + int ret; + + ret = ospf_router_cmd_parse(vty, argv, argc, &instance, &vrf_name); + if (ret != CMD_SUCCESS) + return ret; + + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + ospf = ospf_lookup(instance, vrf_name); + if (ospf) { + if (ospf->gr_info.restart_support) + ospf_gr_nvm_delete(ospf); + + ospf_finish(ospf); + } else + ret = CMD_WARNING_CONFIG_FAILED; + + return ret; +} + + +DEFPY (ospf_router_id, + ospf_router_id_cmd, + "ospf router-id A.B.C.D", + "OSPF specific commands\n" + "router-id for the OSPF process\n" + "OSPF router-id in IP address format\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + struct listnode *node; + struct ospf_area *area; + + ospf->router_id_static = router_id; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) + if (area->full_nbrs) { + vty_out(vty, + "For this router-id change to take effect, use \"clear ip ospf process\" command\n"); + return CMD_SUCCESS; + } + + ospf_router_id_update(ospf); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (ospf_router_id_old, + ospf_router_id_old_cmd, + "router-id A.B.C.D", + "router-id for the OSPF process\n" + "OSPF router-id in IP address format\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4 = 1; + struct listnode *node; + struct ospf_area *area; + struct in_addr router_id; + int ret; + + ret = inet_aton(argv[idx_ipv4]->arg, &router_id); + if (!ret) { + vty_out(vty, "Please specify Router ID by A.B.C.D\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ospf->router_id_static = router_id; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) + if (area->full_nbrs) { + vty_out(vty, + "For this router-id change to take effect, use \"clear ip ospf process\" command\n"); + return CMD_SUCCESS; + } + + ospf_router_id_update(ospf); + + return CMD_SUCCESS; +} + +DEFPY (no_ospf_router_id, + no_ospf_router_id_cmd, + "no ospf router-id [A.B.C.D]", + NO_STR + "OSPF specific commands\n" + "router-id for the OSPF process\n" + "OSPF router-id in IP address format\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct listnode *node; + struct ospf_area *area; + + if (router_id_str) { + if (!IPV4_ADDR_SAME(&ospf->router_id_static, &router_id)) { + vty_out(vty, "%% OSPF router-id doesn't match\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + + ospf->router_id_static.s_addr = 0; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) + if (area->full_nbrs) { + vty_out(vty, + "For this router-id change to take effect, use \"clear ip ospf process\" command\n"); + return CMD_SUCCESS; + } + + ospf_router_id_update(ospf); + + return CMD_SUCCESS; +} + + +static void ospf_passive_interface_default_update(struct ospf *ospf, + uint8_t newval) +{ + struct listnode *ln; + struct ospf_interface *oi; + + ospf->passive_interface_default = newval; + + /* update multicast memberships */ + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, ln, oi)) + ospf_if_set_multicast(oi); +} + +static void ospf_passive_interface_update(struct interface *ifp, + struct ospf_if_params *params, + struct in_addr addr, uint8_t newval) +{ + struct route_node *rn; + + if (OSPF_IF_PARAM_CONFIGURED(params, passive_interface)) { + if (params->passive_interface == newval) + return; + + params->passive_interface = newval; + UNSET_IF_PARAM(params, passive_interface); + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, addr); + ospf_if_update_params(ifp, addr); + } + } else { + params->passive_interface = newval; + 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. + */ + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi = rn->info; + + 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. + */ +} + +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") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf_passive_interface_default_update(ospf, OSPF_IF_PASSIVE); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (ospf_passive_interface_addr, + ospf_passive_interface_addr_cmd, + "passive-interface IFNAME [A.B.C.D]", + "Suppress routing updates on an interface\n" + "Interface's name\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}; + struct ospf_if_params *params; + 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 (ospf->vrf_id != VRF_UNKNOWN) + ifp = if_get_by_name(argv[1]->arg, ospf->vrf_id, ospf->name); + + if (ifp == NULL) { + vty_out(vty, "interface %s not found.\n", (char *)argv[1]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + if (argc == 3) { + 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); + } + + ospf_passive_interface_update(ifp, params, addr, OSPF_IF_PASSIVE); + + return CMD_SUCCESS; +} + +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_HIDDEN (no_ospf_passive_interface, + no_ospf_passive_interface_addr_cmd, + "no passive-interface IFNAME [A.B.C.D]", + NO_STR + "Allow routing updates on an interface\n" + "Interface's name\n" + "IPv4 address\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4 = 3; + struct interface *ifp = NULL; + struct in_addr addr = {.s_addr = INADDR_ANY}; + struct ospf_if_params *params; + int ret; + + 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, ospf->name); + + if (ifp == NULL) { + vty_out(vty, "interface %s not found.\n", (char *)argv[2]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + 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_lookup_if_params(ifp, addr); + if (params == NULL) + return CMD_SUCCESS; + } else { + params = IF_DEF_PARAMS(ifp); + } + + ospf_passive_interface_update(ifp, params, addr, OSPF_IF_ACTIVE); + + return CMD_SUCCESS; +} + + +DEFUN (ospf_network_area, + ospf_network_area_cmd, + "network A.B.C.D/M area ", + "Enable routing on an IP network\n" + "OSPF network prefix\n" + "Set the OSPF area ID\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_prefixlen = 1; + int idx_ipv4_number = 3; + struct prefix_ipv4 p; + struct in_addr area_id; + int ret, format; + uint32_t count; + + if (ospf->instance) { + vty_out(vty, + "The network command is not supported in multi-instance ospf\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + count = ospf_count_area_params(ospf); + if (count > 0) { + vty_out(vty, + "Please remove all ip ospf area x.x.x.x commands first.\n"); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s ospf vrf %s num of %u ip ospf area x config", + __func__, ospf_get_name(ospf), count); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Get network prefix and Area ID. */ + str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p); + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); + + ret = ospf_network_set(ospf, &p, area_id, format); + if (ret == 0) { + vty_out(vty, "There is already same network statement.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_network_area, + no_ospf_network_area_cmd, + "no network A.B.C.D/M area ", + NO_STR + "Enable routing on an IP network\n" + "OSPF network prefix\n" + "Set the OSPF area ID\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_prefixlen = 2; + int idx_ipv4_number = 4; + struct prefix_ipv4 p; + struct in_addr area_id; + int ret, format; + + if (ospf->instance) { + vty_out(vty, + "The network command is not supported in multi-instance ospf\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Get network prefix and Area ID. */ + str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p); + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); + + ret = ospf_network_unset(ospf, &p, area_id); + if (ret == 0) { + vty_out(vty, + "Can't find specified network area configuration.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +DEFUN (ospf_area_range, + ospf_area_range_cmd, + "area range A.B.C.D/M [advertise [cost (0-16777215)]]", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Summarize routes matching address/mask (border routers only)\n" + "Area range prefix\n" + "Advertise this range (default)\n" + "User specified metric for this range\n" + "Advertised metric for this range\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; + int idx_ipv4_number = 1; + int idx_ipv4_prefixlen = 3; + int idx_cost = 6; + struct prefix_ipv4 p; + struct in_addr area_id; + int format; + uint32_t cost; + + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); + str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p); + + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + ospf_area_range_set(ospf, area, area->ranges, &p, + OSPF_AREA_RANGE_ADVERTISE, false); + if (argc > 5) { + cost = strtoul(argv[idx_cost]->arg, NULL, 10); + ospf_area_range_cost_set(ospf, area, area->ranges, &p, cost); + } + + return CMD_SUCCESS; +} + +DEFUN (ospf_area_range_cost, + ospf_area_range_cost_cmd, + "area range A.B.C.D/M {cost (0-16777215)|substitute A.B.C.D/M}", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Summarize routes matching address/mask (border routers only)\n" + "Area range prefix\n" + "User specified metric for this range\n" + "Advertised metric for this range\n" + "Announce area range as another prefix\n" + "Network prefix to be announced instead of range\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; + int idx_ipv4_number = 1; + int idx_ipv4_prefixlen = 3; + int idx = 4; + struct prefix_ipv4 p, s; + struct in_addr area_id; + int format; + uint32_t cost; + + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); + str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p); + + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + ospf_area_range_set(ospf, area, area->ranges, &p, + OSPF_AREA_RANGE_ADVERTISE, false); + if (argv_find(argv, argc, "cost", &idx)) { + cost = strtoul(argv[idx + 1]->arg, NULL, 10); + ospf_area_range_cost_set(ospf, area, area->ranges, &p, cost); + } + + idx = 4; + if (argv_find(argv, argc, "substitute", &idx)) { + str2prefix_ipv4(argv[idx + 1]->arg, &s); + ospf_area_range_substitute_set(ospf, area, &p, &s); + } + + return CMD_SUCCESS; +} + +DEFUN (ospf_area_range_not_advertise, + ospf_area_range_not_advertise_cmd, + "area range A.B.C.D/M not-advertise", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Summarize routes matching address/mask (border routers only)\n" + "Area range prefix\n" + "DoNotAdvertise this range\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; + int idx_ipv4_number = 1; + int idx_ipv4_prefixlen = 3; + struct prefix_ipv4 p; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); + str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p); + + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + ospf_area_range_set(ospf, area, area->ranges, &p, 0, false); + ospf_area_range_substitute_unset(ospf, area, &p); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_range, + no_ospf_area_range_cmd, + "no area range A.B.C.D/M []", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Summarize routes matching address/mask (border routers only)\n" + "Area range prefix\n" + "User specified metric for this range\n" + "Advertised metric for this range\n" + "Advertise this range (default)\n" + "User specified metric for this range\n" + "Advertised metric for this range\n" + "DoNotAdvertise this range\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; + int idx_ipv4_number = 2; + int idx_ipv4_prefixlen = 4; + struct prefix_ipv4 p; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); + str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p); + + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + ospf_area_range_unset(ospf, area, area->ranges, &p); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_range_substitute, + no_ospf_area_range_substitute_cmd, + "no area range A.B.C.D/M substitute A.B.C.D/M", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Summarize routes matching address/mask (border routers only)\n" + "Area range prefix\n" + "Announce area range as another prefix\n" + "Network prefix to be announced instead of range\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; + int idx_ipv4_number = 2; + int idx_ipv4_prefixlen = 4; + int idx_ipv4_prefixlen_2 = 6; + struct prefix_ipv4 p, s; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); + str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p); + str2prefix_ipv4(argv[idx_ipv4_prefixlen_2]->arg, &s); + + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + ospf_area_range_substitute_unset(ospf, area, &p); + + return CMD_SUCCESS; +} + + +/* Command Handler Logic in VLink stuff is delicate!! + + ALTER AT YOUR OWN RISK!!!! + + Various dummy values are used to represent 'NoChange' state for + VLink configuration NOT being changed by a VLink command, and + special syntax is used within the command strings so that the + typed in command verbs can be seen in the configuration command + bacckend handler. This is to drastically reduce the verbeage + required to coe up with a reasonably compatible Cisco VLink command + + - Matthew Grant + Wed, 21 Feb 2001 15:13:52 +1300 + */ + +/* Configuration data for virtual links + */ +struct ospf_vl_config_data { + struct vty *vty; /* vty stuff */ + struct in_addr area_id; /* area ID from command line */ + int area_id_fmt; /* command line area ID format */ + struct in_addr vl_peer; /* command line vl_peer */ + int auth_type; /* Authehntication type, if given */ + char *auth_key; /* simple password if present */ + int crypto_key_id; /* Cryptographic key ID */ + char *md5_key; /* MD5 authentication key */ + char *keychain; /* Cryptographic keychain */ + int del_keychain; + int hello_interval; /* Obvious what these are... */ + int retransmit_interval; + int transmit_delay; + int dead_interval; +}; + +static void ospf_vl_config_data_init(struct ospf_vl_config_data *vl_config, + struct vty *vty) +{ + memset(vl_config, 0, sizeof(struct ospf_vl_config_data)); + vl_config->auth_type = OSPF_AUTH_CMD_NOTSEEN; + vl_config->vty = vty; +} + +static struct ospf_vl_data * +ospf_find_vl_data(struct ospf *ospf, struct ospf_vl_config_data *vl_config) +{ + struct ospf_area *area; + struct ospf_vl_data *vl_data; + struct vty *vty; + struct in_addr area_id; + + vty = vl_config->vty; + area_id = vl_config->area_id; + + if (area_id.s_addr == OSPF_AREA_BACKBONE) { + vty_out(vty, + "Configuring VLs over the backbone is not allowed\n"); + return NULL; + } + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, vl_config->area_id_fmt); + + if (area->external_routing != OSPF_AREA_DEFAULT) { + if (vl_config->area_id_fmt == OSPF_AREA_ID_FMT_DOTTEDQUAD) + vty_out(vty, "Area %pI4 is %s\n", &area_id, + area->external_routing == OSPF_AREA_NSSA + ? "nssa" + : "stub"); + else + vty_out(vty, "Area %ld is %s\n", + (unsigned long)ntohl(area_id.s_addr), + area->external_routing == OSPF_AREA_NSSA + ? "nssa" + : "stub"); + return NULL; + } + + if ((vl_data = ospf_vl_lookup(ospf, area, vl_config->vl_peer)) + == NULL) { + vl_data = ospf_vl_data_new(area, vl_config->vl_peer); + if (vl_data->vl_oi == NULL) { + vl_data->vl_oi = ospf_vl_new(ospf, vl_data); + ospf_vl_add(ospf, vl_data); + ospf_spf_calculate_schedule(ospf, + SPF_FLAG_CONFIG_CHANGE); + } + } + return vl_data; +} + + +static int ospf_vl_set_security(struct ospf_vl_data *vl_data, + struct ospf_vl_config_data *vl_config) +{ + struct crypt_key *ck; + struct vty *vty; + struct interface *ifp = vl_data->vl_oi->ifp; + + vty = vl_config->vty; + + if (vl_config->auth_type != OSPF_AUTH_CMD_NOTSEEN) { + SET_IF_PARAM(IF_DEF_PARAMS(ifp), auth_type); + IF_DEF_PARAMS(ifp)->auth_type = vl_config->auth_type; + } + + if (vl_config->auth_key) { + memset(IF_DEF_PARAMS(ifp)->auth_simple, 0, + OSPF_AUTH_SIMPLE_SIZE + 1); + strlcpy((char *)IF_DEF_PARAMS(ifp)->auth_simple, + vl_config->auth_key, + sizeof(IF_DEF_PARAMS(ifp)->auth_simple)); + } else if (vl_config->keychain) { + SET_IF_PARAM(IF_DEF_PARAMS(ifp), keychain_name); + XFREE(MTYPE_OSPF_IF_PARAMS, IF_DEF_PARAMS(ifp)->keychain_name); + IF_DEF_PARAMS(ifp)->keychain_name = XSTRDUP(MTYPE_OSPF_IF_PARAMS, vl_config->keychain); + } else if (vl_config->md5_key) { + if (ospf_crypt_key_lookup(IF_DEF_PARAMS(ifp)->auth_crypt, + vl_config->crypto_key_id) + != NULL) { + vty_out(vty, "OSPF: Key %d already exists\n", + vl_config->crypto_key_id); + return CMD_WARNING; + } + ck = ospf_crypt_key_new(); + ck->key_id = vl_config->crypto_key_id; + memset(ck->auth_key, 0, OSPF_AUTH_MD5_SIZE + 1); + strlcpy((char *)ck->auth_key, vl_config->md5_key, + sizeof(ck->auth_key)); + + ospf_crypt_key_add(IF_DEF_PARAMS(ifp)->auth_crypt, ck); + } else if (vl_config->crypto_key_id != 0) { + /* Delete a key */ + + if (ospf_crypt_key_lookup(IF_DEF_PARAMS(ifp)->auth_crypt, + vl_config->crypto_key_id) + == NULL) { + vty_out(vty, "OSPF: Key %d does not exist\n", + vl_config->crypto_key_id); + return CMD_WARNING_CONFIG_FAILED; + } + + ospf_crypt_key_delete(IF_DEF_PARAMS(ifp)->auth_crypt, + vl_config->crypto_key_id); + } else if (vl_config->del_keychain) { + UNSET_IF_PARAM(IF_DEF_PARAMS(ifp), keychain_name); + XFREE(MTYPE_OSPF_IF_PARAMS, IF_DEF_PARAMS(ifp)->keychain_name); + } + + return CMD_SUCCESS; +} + +static int ospf_vl_set_timers(struct ospf_vl_data *vl_data, + struct ospf_vl_config_data *vl_config) +{ + struct interface *ifp = vl_data->vl_oi->ifp; + /* Virtual Link data initialised to defaults, so only set + if a value given */ + if (vl_config->hello_interval) { + SET_IF_PARAM(IF_DEF_PARAMS(ifp), v_hello); + IF_DEF_PARAMS(ifp)->v_hello = vl_config->hello_interval; + } + + if (vl_config->dead_interval) { + SET_IF_PARAM(IF_DEF_PARAMS(ifp), v_wait); + IF_DEF_PARAMS(ifp)->v_wait = vl_config->dead_interval; + } + + if (vl_config->retransmit_interval) { + SET_IF_PARAM(IF_DEF_PARAMS(ifp), retransmit_interval); + IF_DEF_PARAMS(ifp)->retransmit_interval = + vl_config->retransmit_interval; + } + + if (vl_config->transmit_delay) { + SET_IF_PARAM(IF_DEF_PARAMS(ifp), transmit_delay); + IF_DEF_PARAMS(ifp)->transmit_delay = vl_config->transmit_delay; + } + + return CMD_SUCCESS; +} + + +/* The business end of all of the above */ +static int ospf_vl_set(struct ospf *ospf, struct ospf_vl_config_data *vl_config) +{ + struct ospf_vl_data *vl_data; + int ret; + + vl_data = ospf_find_vl_data(ospf, vl_config); + if (!vl_data) + return CMD_WARNING_CONFIG_FAILED; + + /* Process this one first as it can have a fatal result, which can + only logically occur if the virtual link exists already + Thus a command error does not result in a change to the + running configuration such as unexpectedly altered timer + values etc.*/ + ret = ospf_vl_set_security(vl_data, vl_config); + if (ret != CMD_SUCCESS) + return ret; + + /* Set any time based parameters, these area already range checked */ + + ret = ospf_vl_set_timers(vl_data, vl_config); + if (ret != CMD_SUCCESS) + return ret; + + return CMD_SUCCESS; +} + +/* This stuff exists to make specifying all the alias commands A LOT simpler + */ +#define VLINK_HELPSTR_IPADDR \ + "OSPF area parameters\n" \ + "OSPF area ID in IP address format\n" \ + "OSPF area ID as a decimal value\n" \ + "Configure a virtual link\n" \ + "Router ID of the remote ABR\n" + +#define VLINK_HELPSTR_AUTHTYPE_SIMPLE \ + "Enable authentication on this virtual link\n" \ + "dummy string \n" + +#define VLINK_HELPSTR_AUTHTYPE_ALL \ + VLINK_HELPSTR_AUTHTYPE_SIMPLE \ + "Use null authentication\n" \ + "Use message-digest authentication\n" + +#define VLINK_HELPSTR_TIME_PARAM \ + "Time between HELLO packets\n" \ + "Seconds\n" \ + "Time between retransmitting lost link state advertisements\n" \ + "Seconds\n" \ + "Link state transmit delay\n" \ + "Seconds\n" \ + "Interval time after which a neighbor is declared down\n" \ + "Seconds\n" + +#define VLINK_HELPSTR_AUTH_SIMPLE \ + "Authentication password (key)\n" \ + "The OSPF password (key)\n" + +#define VLINK_HELPSTR_AUTH_MD5 \ + "Message digest authentication password (key)\n" \ + "Key ID\n" \ + "Use MD5 algorithm\n" \ + "The OSPF password (key)\n" + +DEFUN (ospf_area_vlink, + ospf_area_vlink_cmd, + "area virtual-link A.B.C.D [authentication []] []", + VLINK_HELPSTR_IPADDR + "Enable authentication on this virtual link\n" + "Use a key-chain for cryptographic authentication keys\n" + "Key-chain name\n" + "Use message-digest authentication\n" + "Use null authentication\n" + VLINK_HELPSTR_AUTH_MD5 + VLINK_HELPSTR_AUTH_SIMPLE) +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 1; + int idx_ipv4 = 3; + struct ospf_vl_config_data vl_config; + char auth_key[OSPF_AUTH_SIMPLE_SIZE + 1]; + char md5_key[OSPF_AUTH_MD5_SIZE + 1]; + int ret; + int idx = 0; + + ospf_vl_config_data_init(&vl_config, vty); + + /* Read off first 2 parameters and check them */ + ret = str2area_id(argv[idx_ipv4_number]->arg, &vl_config.area_id, + &vl_config.area_id_fmt); + if (ret < 0) { + vty_out(vty, "OSPF area ID is invalid\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ret = inet_aton(argv[idx_ipv4]->arg, &vl_config.vl_peer); + if (!ret) { + vty_out(vty, "Please specify valid Router ID as a.b.c.d\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (argc <= 4) { + /* Thats all folks! - BUGS B. strikes again!!!*/ + + return ospf_vl_set(ospf, &vl_config); + } + + if (argv_find(argv, argc, "authentication", &idx)) { + /* authentication - this option can only occur + at start of command line */ + vl_config.auth_type = OSPF_AUTH_SIMPLE; + } + + if (argv_find(argv, argc, "key-chain", &idx)) { + vl_config.auth_type = OSPF_AUTH_CRYPTOGRAPHIC; + vl_config.keychain = argv[idx+1]->arg; + } else if (argv_find(argv, argc, "message-digest", &idx)) { + /* authentication message-digest */ + vl_config.auth_type = OSPF_AUTH_CRYPTOGRAPHIC; + } else if (argv_find(argv, argc, "null", &idx)) { + /* "authentication null" */ + vl_config.auth_type = OSPF_AUTH_NULL; + } + + if (argv_find(argv, argc, "message-digest-key", &idx)) { + vl_config.md5_key = NULL; + vl_config.crypto_key_id = strtol(argv[idx + 1]->arg, NULL, 10); + if (vl_config.crypto_key_id < 0) + return CMD_WARNING_CONFIG_FAILED; + + strlcpy(md5_key, argv[idx + 3]->arg, sizeof(md5_key)); + vl_config.md5_key = md5_key; + } + + if (argv_find(argv, argc, "authentication-key", &idx)) { + strlcpy(auth_key, argv[idx + 1]->arg, sizeof(auth_key)); + vl_config.auth_key = auth_key; + } + + /* Action configuration */ + + return ospf_vl_set(ospf, &vl_config); +} + +DEFUN (no_ospf_area_vlink, + no_ospf_area_vlink_cmd, + "no area virtual-link A.B.C.D [authentication []] []", + NO_STR + VLINK_HELPSTR_IPADDR + "Enable authentication on this virtual link\n" + "Use a key-chain for cryptographic authentication keys\n" + "Key-chain name\n" + "Use message-digest authentication\n" + "Use null authentication\n" + VLINK_HELPSTR_AUTH_MD5 + VLINK_HELPSTR_AUTH_SIMPLE) +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 2; + int idx_ipv4 = 4; + struct ospf_area *area; + struct ospf_vl_config_data vl_config; + struct ospf_vl_data *vl_data = NULL; + char auth_key[OSPF_AUTH_SIMPLE_SIZE + 1]; + int idx = 0; + int ret, format; + + ospf_vl_config_data_init(&vl_config, vty); + + ret = str2area_id(argv[idx_ipv4_number]->arg, &vl_config.area_id, + &format); + if (ret < 0) { + vty_out(vty, "OSPF area ID is invalid\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + area = ospf_area_lookup_by_area_id(ospf, vl_config.area_id); + if (!area) { + vty_out(vty, "Area does not exist\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ret = inet_aton(argv[idx_ipv4]->arg, &vl_config.vl_peer); + if (!ret) { + vty_out(vty, "Please specify valid Router ID as a.b.c.d\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + vl_data = ospf_vl_lookup(ospf, area, vl_config.vl_peer); + if (!vl_data) { + vty_out(vty, "Virtual link does not exist\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (argc <= 5) { + /* Basic VLink no command */ + /* Thats all folks! - BUGS B. strikes again!!!*/ + ospf_vl_delete(ospf, vl_data); + ospf_area_check_free(ospf, vl_config.area_id); + return CMD_SUCCESS; + } + + /* If we are down here, we are reseting parameters */ + /* Deal with other parameters */ + + if (argv_find(argv, argc, "authentication", &idx)) { + /* authentication - this option can only occur + at start of command line */ + vl_config.auth_type = OSPF_AUTH_NOTSET; + } + + if (argv_find(argv, argc, "key-chain", &idx)) { + vl_config.del_keychain = 1; + vl_config.keychain = NULL; + } + + if (argv_find(argv, argc, "message-digest-key", &idx)) { + vl_config.md5_key = NULL; + vl_config.crypto_key_id = strtol(argv[idx + 1]->arg, NULL, 10); + if (vl_config.crypto_key_id < 0) + return CMD_WARNING_CONFIG_FAILED; + } + + if (argv_find(argv, argc, "authentication-key", &idx)) { + /* Reset authentication-key to 0 */ + memset(auth_key, 0, OSPF_AUTH_SIMPLE_SIZE + 1); + vl_config.auth_key = auth_key; + } + + /* Action configuration */ + + return ospf_vl_set(ospf, &vl_config); +} + +DEFUN (ospf_area_vlink_intervals, + ospf_area_vlink_intervals_cmd, + "area virtual-link A.B.C.D {hello-interval (1-65535)|retransmit-interval (1-65535)|transmit-delay (1-65535)|dead-interval (1-65535)}", + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_TIME_PARAM) +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_vl_config_data vl_config; + int ret = 0; + + ospf_vl_config_data_init(&vl_config, vty); + + char *area_id = argv[1]->arg; + char *router_id = argv[3]->arg; + + ret = str2area_id(area_id, &vl_config.area_id, &vl_config.area_id_fmt); + if (ret < 0) { + vty_out(vty, "OSPF area ID is invalid\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ret = inet_aton(router_id, &vl_config.vl_peer); + if (!ret) { + vty_out(vty, "Please specify valid Router ID as a.b.c.d\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + for (int idx = 4; idx < argc; idx++) { + if (strmatch(argv[idx]->text, "hello-interval")) + vl_config.hello_interval = + strtol(argv[++idx]->arg, NULL, 10); + else if (strmatch(argv[idx]->text, "retransmit-interval")) + vl_config.retransmit_interval = + strtol(argv[++idx]->arg, NULL, 10); + else if (strmatch(argv[idx]->text, "transmit-delay")) + vl_config.transmit_delay = + strtol(argv[++idx]->arg, NULL, 10); + else if (strmatch(argv[idx]->text, "dead-interval")) + vl_config.dead_interval = + strtol(argv[++idx]->arg, NULL, 10); + } + + /* Action configuration */ + return ospf_vl_set(ospf, &vl_config); +} + +DEFUN (no_ospf_area_vlink_intervals, + no_ospf_area_vlink_intervals_cmd, + "no area virtual-link A.B.C.D {hello-interval (1-65535)|retransmit-interval (1-65535)|transmit-delay (1-65535)|dead-interval (1-65535)}", + NO_STR + VLINK_HELPSTR_IPADDR + VLINK_HELPSTR_TIME_PARAM) +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_vl_config_data vl_config; + int ret = 0; + + ospf_vl_config_data_init(&vl_config, vty); + + char *area_id = argv[2]->arg; + char *router_id = argv[4]->arg; + + ret = str2area_id(area_id, &vl_config.area_id, &vl_config.area_id_fmt); + if (ret < 0) { + vty_out(vty, "OSPF area ID is invalid\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ret = inet_aton(router_id, &vl_config.vl_peer); + if (!ret) { + vty_out(vty, "Please specify valid Router ID as a.b.c.d\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + for (int idx = 5; idx < argc; idx++) { + if (strmatch(argv[idx]->text, "hello-interval")) + vl_config.hello_interval = OSPF_HELLO_INTERVAL_DEFAULT; + else if (strmatch(argv[idx]->text, "retransmit-interval")) + vl_config.retransmit_interval = + OSPF_RETRANSMIT_INTERVAL_DEFAULT; + else if (strmatch(argv[idx]->text, "transmit-delay")) + vl_config.transmit_delay = OSPF_TRANSMIT_DELAY_DEFAULT; + else if (strmatch(argv[idx]->text, "dead-interval")) + vl_config.dead_interval = + OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; + } + + /* Action configuration */ + return ospf_vl_set(ospf, &vl_config); +} + +DEFUN (ospf_area_shortcut, + ospf_area_shortcut_cmd, + "area shortcut ", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure the area's shortcutting mode\n" + "Set default shortcutting behavior\n" + "Enable shortcutting through the area\n" + "Disable shortcutting through the area\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 1; + int idx_enable_disable = 3; + struct ospf_area *area; + struct in_addr area_id; + int mode; + int format; + + VTY_GET_OSPF_AREA_ID_NO_BB("shortcut", area_id, format, + argv[idx_ipv4_number]->arg); + + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + if (strncmp(argv[idx_enable_disable]->arg, "de", 2) == 0) + mode = OSPF_SHORTCUT_DEFAULT; + else if (strncmp(argv[idx_enable_disable]->arg, "di", 2) == 0) + mode = OSPF_SHORTCUT_DISABLE; + else if (strncmp(argv[idx_enable_disable]->arg, "e", 1) == 0) + mode = OSPF_SHORTCUT_ENABLE; + else + return CMD_WARNING_CONFIG_FAILED; + + ospf_area_shortcut_set(ospf, area, mode); + + if (ospf->abr_type != OSPF_ABR_SHORTCUT) + vty_out(vty, + "Shortcut area setting will take effect only when the router is configured as Shortcut ABR\n"); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_shortcut, + no_ospf_area_shortcut_cmd, + "no area shortcut ", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Deconfigure the area's shortcutting mode\n" + "Deconfigure enabled shortcutting through the area\n" + "Deconfigure disabled shortcutting through the area\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 2; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID_NO_BB("shortcut", area_id, format, + argv[idx_ipv4_number]->arg); + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (!area) + return CMD_SUCCESS; + + ospf_area_shortcut_unset(ospf, area); + + return CMD_SUCCESS; +} + + +DEFUN (ospf_area_stub, + ospf_area_stub_cmd, + "area stub", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as stub\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 1; + struct in_addr area_id; + int ret, format; + + VTY_GET_OSPF_AREA_ID_NO_BB("stub", area_id, format, + argv[idx_ipv4_number]->arg); + + ret = ospf_area_stub_set(ospf, area_id); + ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), + format); + if (ret == 0) { + vty_out(vty, + "First deconfigure all virtual link through this area\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Flush the external LSAs from the specified area */ + ospf_flush_lsa_from_area(ospf, area_id, OSPF_AS_EXTERNAL_LSA); + ospf_area_no_summary_unset(ospf, area_id); + + return CMD_SUCCESS; +} + +DEFUN (ospf_area_stub_no_summary, + ospf_area_stub_no_summary_cmd, + "area stub no-summary", + "OSPF stub parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as stub\n" + "Do not inject inter-area routes into stub\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 1; + struct in_addr area_id; + int ret, format; + + VTY_GET_OSPF_AREA_ID_NO_BB("stub", area_id, format, + argv[idx_ipv4_number]->arg); + + ret = ospf_area_stub_set(ospf, area_id); + ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), + format); + if (ret == 0) { + vty_out(vty, + "%% Area cannot be stub as it contains a virtual link\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ospf_area_no_summary_set(ospf, area_id); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_stub, + no_ospf_area_stub_cmd, + "no area stub", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as stub\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 2; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID_NO_BB("stub", area_id, format, + argv[idx_ipv4_number]->arg); + + ospf_area_stub_unset(ospf, area_id); + ospf_area_no_summary_unset(ospf, area_id); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_stub_no_summary, + no_ospf_area_stub_no_summary_cmd, + "no area stub no-summary", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as stub\n" + "Do not inject inter-area routes into area\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 2; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID_NO_BB("stub", area_id, format, + argv[idx_ipv4_number]->arg); + ospf_area_no_summary_unset(ospf, area_id); + + return CMD_SUCCESS; +} + +DEFPY (ospf_area_nssa, + ospf_area_nssa_cmd, + "area $area_str nssa\ + [{\ + $translator_role\ + |default-information-originate$dflt_originate [{metric (0-16777214)$mval|metric-type (1-2)$mtype}]\ + |no-summary$no_summary\ + |suppress-fa$suppress_fa\ + }]", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as nssa\n" + "Configure NSSA-ABR for translate election (default)\n" + "Configure NSSA-ABR to never translate\n" + "Configure NSSA-ABR to always translate\n" + "Originate Type 7 default into NSSA area\n" + "OSPF default metric\n" + "OSPF metric\n" + "OSPF metric type for default routes\n" + "Set OSPF External Type 1/2 metrics\n" + "Do not inject inter-area routes into nssa\n" + "Suppress forwarding address\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct in_addr area_id; + int ret, format; + + VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, area_str); + + ret = ospf_area_nssa_set(ospf, area_id); + ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), + format); + if (ret == 0) { + vty_out(vty, + "%% Area cannot be nssa as it contains a virtual link\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (translator_role) { + if (strncmp(translator_role, "translate-c", 11) == 0) + ospf_area_nssa_translator_role_set( + ospf, area_id, OSPF_NSSA_ROLE_CANDIDATE); + else if (strncmp(translator_role, "translate-n", 11) == 0) + ospf_area_nssa_translator_role_set( + ospf, area_id, OSPF_NSSA_ROLE_NEVER); + else if (strncmp(translator_role, "translate-a", 11) == 0) + ospf_area_nssa_translator_role_set( + ospf, area_id, OSPF_NSSA_ROLE_ALWAYS); + } else { + ospf_area_nssa_translator_role_set(ospf, area_id, + OSPF_NSSA_ROLE_CANDIDATE); + } + + if (dflt_originate) { + int metric_type = DEFAULT_METRIC_TYPE; + + if (mval_str == NULL) + mval = -1; + if (mtype_str) + (void)str2metric_type(mtype_str, &metric_type); + ospf_area_nssa_default_originate_set(ospf, area_id, mval, + metric_type); + } else + ospf_area_nssa_default_originate_unset(ospf, area_id); + + if (no_summary) + ospf_area_nssa_no_summary_set(ospf, area_id); + else + ospf_area_no_summary_unset(ospf, area_id); + + if (suppress_fa) + ospf_area_nssa_suppress_fa_set(ospf, area_id); + else + ospf_area_nssa_suppress_fa_unset(ospf, area_id); + + /* Flush the external LSA for the specified area */ + ospf_flush_lsa_from_area(ospf, area_id, OSPF_AS_EXTERNAL_LSA); + ospf_schedule_abr_task(ospf); + ospf_schedule_asbr_redist_update(ospf); + + return CMD_SUCCESS; +} + +DEFPY (no_ospf_area_nssa, + no_ospf_area_nssa_cmd, + "no area $area_str nssa\ + [{\ + \ + |default-information-originate [{metric (0-16777214)|metric-type (1-2)}]\ + |no-summary\ + |suppress-fa\ + }]", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as nssa\n" + "Configure NSSA-ABR for translate election (default)\n" + "Configure NSSA-ABR to never translate\n" + "Configure NSSA-ABR to always translate\n" + "Originate Type 7 default into NSSA area\n" + "OSPF default metric\n" + "OSPF metric\n" + "OSPF metric type for default routes\n" + "Set OSPF External Type 1/2 metrics\n" + "Do not inject inter-area routes into nssa\n" + "Suppress forwarding address\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, area_str); + + /* Flush the NSSA LSA for the specified area */ + ospf_flush_lsa_from_area(ospf, area_id, OSPF_AS_NSSA_LSA); + ospf_area_no_summary_unset(ospf, area_id); + ospf_area_nssa_default_originate_unset(ospf, area_id); + ospf_area_nssa_suppress_fa_unset(ospf, area_id); + ospf_area_nssa_unset(ospf, area_id); + + ospf_schedule_abr_task(ospf); + + return CMD_SUCCESS; +} + +DEFPY (ospf_area_nssa_range, + ospf_area_nssa_range_cmd, + "area $area_str nssa range A.B.C.D/M$prefix []", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as nssa\n" + "Configured address range\n" + "Specify IPv4 prefix\n" + "Do not advertise\n" + "User specified metric for this range\n" + "Advertised metric for this range\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; + struct in_addr area_id; + int format; + int advertise = 0; + + VTY_GET_OSPF_AREA_ID(area_id, format, area_str); + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + if (area->external_routing != OSPF_AREA_NSSA) { + vty_out(vty, "%% First configure %s as an NSSA area\n", + area_str); + return CMD_WARNING; + } + + if (!not_adv) + advertise = OSPF_AREA_RANGE_ADVERTISE; + + ospf_area_range_set(ospf, area, area->nssa_ranges, + (struct prefix_ipv4 *)prefix, advertise, true); + if (cost_str) + ospf_area_range_cost_set(ospf, area, area->nssa_ranges, + (struct prefix_ipv4 *)prefix, cost); + + return CMD_SUCCESS; +} + +DEFPY (no_ospf_area_nssa_range, + no_ospf_area_nssa_range_cmd, + "no area $area_str nssa range A.B.C.D/M$prefix []", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as nssa\n" + "Configured address range\n" + "Specify IPv4 prefix\n" + "Do not advertise\n" + "User specified metric for this range\n" + "Advertised metric for this range\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID(area_id, format, area_str); + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + if (area->external_routing != OSPF_AREA_NSSA) { + vty_out(vty, "%% First configure %s as an NSSA area\n", + area_str); + return CMD_WARNING; + } + + ospf_area_range_unset(ospf, area, area->nssa_ranges, + (struct prefix_ipv4 *)prefix); + + return CMD_SUCCESS; +} + +DEFUN (ospf_area_default_cost, + ospf_area_default_cost_cmd, + "area default-cost (0-16777215)", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Set the summary-default cost of a NSSA or stub area\n" + "Stub's advertised default summary cost\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 1; + int idx_number = 3; + struct ospf_area *area; + struct in_addr area_id; + uint32_t cost; + int format; + struct prefix_ipv4 p; + + VTY_GET_OSPF_AREA_ID_NO_BB("default-cost", area_id, format, + argv[idx_ipv4_number]->arg); + cost = strtoul(argv[idx_number]->arg, NULL, 10); + + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + if (area->external_routing == OSPF_AREA_DEFAULT) { + vty_out(vty, "The area is neither stub, nor NSSA\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + area->default_cost = cost; + + p.family = AF_INET; + p.prefix.s_addr = OSPF_DEFAULT_DESTINATION; + p.prefixlen = 0; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "ospf_abr_announce_stub_defaults(): announcing 0.0.0.0/0 to area %pI4", + &area->area_id); + ospf_abr_announce_network_to_area(&p, area->default_cost, area); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_default_cost, + no_ospf_area_default_cost_cmd, + "no area default-cost (0-16777215)", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Set the summary-default cost of a NSSA or stub area\n" + "Stub's advertised default summary cost\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 2; + struct ospf_area *area; + struct in_addr area_id; + int format; + struct prefix_ipv4 p; + + VTY_GET_OSPF_AREA_ID_NO_BB("default-cost", area_id, format, + argv[idx_ipv4_number]->arg); + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return CMD_SUCCESS; + + if (area->external_routing == OSPF_AREA_DEFAULT) { + vty_out(vty, "The area is neither stub, nor NSSA\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + area->default_cost = 1; + + p.family = AF_INET; + p.prefix.s_addr = OSPF_DEFAULT_DESTINATION; + p.prefixlen = 0; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "ospf_abr_announce_stub_defaults(): announcing 0.0.0.0/0 to area %pI4", + &area->area_id); + ospf_abr_announce_network_to_area(&p, area->default_cost, area); + + + ospf_area_check_free(ospf, area_id); + + return CMD_SUCCESS; +} + +DEFUN (ospf_area_export_list, + ospf_area_export_list_cmd, + "area export-list ACCESSLIST4_NAME", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Set the filter for networks announced to other areas\n" + "Name of the access-list\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 1; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); + + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + ospf_area_export_list_set(ospf, area, argv[3]->arg); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_export_list, + no_ospf_area_export_list_cmd, + "no area export-list ACCESSLIST4_NAME", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Unset the filter for networks announced to other areas\n" + "Name of the access-list\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 2; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return CMD_SUCCESS; + + ospf_area_export_list_unset(ospf, area); + + return CMD_SUCCESS; +} + + +DEFUN (ospf_area_import_list, + ospf_area_import_list_cmd, + "area import-list ACCESSLIST4_NAME", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Set the filter for networks from other areas announced to the specified one\n" + "Name of the access-list\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 1; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); + + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + ospf_area_import_list_set(ospf, area, argv[3]->arg); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_import_list, + no_ospf_area_import_list_cmd, + "no area import-list ACCESSLIST4_NAME", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Unset the filter for networks announced to other areas\n" + "Name of the access-list\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 2; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return CMD_SUCCESS; + + ospf_area_import_list_unset(ospf, area); + + return CMD_SUCCESS; +} + +DEFUN (ospf_area_filter_list, + ospf_area_filter_list_cmd, + "area filter-list prefix PREFIXLIST_NAME ", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Filter networks between OSPF areas\n" + "Filter prefixes between OSPF areas\n" + "Name of an IP prefix-list\n" + "Filter networks sent to this area\n" + "Filter networks sent from this area\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 1; + int idx_word = 4; + int idx_in_out = 5; + struct ospf_area *area; + struct in_addr area_id; + struct prefix_list *plist; + int format; + + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); + + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + plist = prefix_list_lookup(AFI_IP, argv[idx_word]->arg); + if (strncmp(argv[idx_in_out]->arg, "in", 2) == 0) { + PREFIX_LIST_IN(area) = plist; + if (PREFIX_NAME_IN(area)) + free(PREFIX_NAME_IN(area)); + + PREFIX_NAME_IN(area) = strdup(argv[idx_word]->arg); + ospf_schedule_abr_task(ospf); + } else { + PREFIX_LIST_OUT(area) = plist; + if (PREFIX_NAME_OUT(area)) + free(PREFIX_NAME_OUT(area)); + + PREFIX_NAME_OUT(area) = strdup(argv[idx_word]->arg); + ospf_schedule_abr_task(ospf); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_filter_list, + no_ospf_area_filter_list_cmd, + "no area filter-list prefix PREFIXLIST_NAME ", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Filter networks between OSPF areas\n" + "Filter prefixes between OSPF areas\n" + "Name of an IP prefix-list\n" + "Filter networks sent to this area\n" + "Filter networks sent from this area\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 2; + int idx_word = 5; + int idx_in_out = 6; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); + + if ((area = ospf_area_lookup_by_area_id(ospf, area_id)) == NULL) + return CMD_SUCCESS; + + if (strncmp(argv[idx_in_out]->arg, "in", 2) == 0) { + if (PREFIX_NAME_IN(area)) + if (strcmp(PREFIX_NAME_IN(area), argv[idx_word]->arg) + != 0) + return CMD_SUCCESS; + + PREFIX_LIST_IN(area) = NULL; + if (PREFIX_NAME_IN(area)) + free(PREFIX_NAME_IN(area)); + + PREFIX_NAME_IN(area) = NULL; + + ospf_schedule_abr_task(ospf); + } else { + if (PREFIX_NAME_OUT(area)) + if (strcmp(PREFIX_NAME_OUT(area), argv[idx_word]->arg) + != 0) + return CMD_SUCCESS; + + PREFIX_LIST_OUT(area) = NULL; + if (PREFIX_NAME_OUT(area)) + free(PREFIX_NAME_OUT(area)); + + PREFIX_NAME_OUT(area) = NULL; + + ospf_schedule_abr_task(ospf); + } + + return CMD_SUCCESS; +} + + +DEFUN (ospf_area_authentication_message_digest, + ospf_area_authentication_message_digest_cmd, + "[no] area authentication message-digest", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Enable authentication\n" + "Use message-digest authentication\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx = 0; + struct ospf_area *area; + struct in_addr area_id; + int format; + + argv_find(argv, argc, "area", &idx); + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx + 1]->arg); + + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + area->auth_type = strmatch(argv[0]->text, "no") + ? OSPF_AUTH_NULL + : OSPF_AUTH_CRYPTOGRAPHIC; + + return CMD_SUCCESS; +} + +DEFUN (ospf_area_authentication, + ospf_area_authentication_cmd, + "area authentication", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Enable authentication\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 1; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); + + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + area->auth_type = OSPF_AUTH_SIMPLE; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_area_authentication, + no_ospf_area_authentication_cmd, + "no area authentication", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Enable authentication\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4_number = 2; + struct ospf_area *area; + struct in_addr area_id; + int format; + + VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return CMD_SUCCESS; + + area->auth_type = OSPF_AUTH_NULL; + + ospf_area_check_free(ospf, area_id); + + return CMD_SUCCESS; +} + + +DEFUN (ospf_abr_type, + ospf_abr_type_cmd, + "ospf abr-type ", + "OSPF specific commands\n" + "Set OSPF ABR type\n" + "Alternative ABR, cisco implementation\n" + "Alternative ABR, IBM implementation\n" + "Shortcut ABR\n" + "Standard behavior (RFC2328)\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_vendor = 2; + uint8_t abr_type = OSPF_ABR_UNKNOWN; + + if (strncmp(argv[idx_vendor]->arg, "c", 1) == 0) + abr_type = OSPF_ABR_CISCO; + else if (strncmp(argv[idx_vendor]->arg, "i", 1) == 0) + abr_type = OSPF_ABR_IBM; + else if (strncmp(argv[idx_vendor]->arg, "sh", 2) == 0) + abr_type = OSPF_ABR_SHORTCUT; + else if (strncmp(argv[idx_vendor]->arg, "st", 2) == 0) + abr_type = OSPF_ABR_STAND; + else + return CMD_WARNING_CONFIG_FAILED; + + /* If ABR type value is changed, schedule ABR task. */ + if (ospf->abr_type != abr_type) { + ospf->abr_type = abr_type; + ospf_schedule_abr_task(ospf); + + /* The ABR task might not initiate SPF recalculation if the + * OSPF flags remain the same. And inter-area routes would not + * be added/deleted according to the new ABR type. So this + * needs to be done here too. + */ + ospf_spf_calculate_schedule(ospf, SPF_FLAG_ABR_STATUS_CHANGE); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_abr_type, + no_ospf_abr_type_cmd, + "no ospf abr-type ", + NO_STR + "OSPF specific commands\n" + "Set OSPF ABR type\n" + "Alternative ABR, cisco implementation\n" + "Alternative ABR, IBM implementation\n" + "Shortcut ABR\n" + "Standard ABR\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_vendor = 3; + uint8_t abr_type = OSPF_ABR_UNKNOWN; + + if (strncmp(argv[idx_vendor]->arg, "c", 1) == 0) + abr_type = OSPF_ABR_CISCO; + else if (strncmp(argv[idx_vendor]->arg, "i", 1) == 0) + abr_type = OSPF_ABR_IBM; + else if (strncmp(argv[idx_vendor]->arg, "sh", 2) == 0) + abr_type = OSPF_ABR_SHORTCUT; + else if (strncmp(argv[idx_vendor]->arg, "st", 2) == 0) + abr_type = OSPF_ABR_STAND; + else + return CMD_WARNING_CONFIG_FAILED; + + /* If ABR type value is changed, schedule ABR task. */ + if (ospf->abr_type == abr_type) { + ospf->abr_type = OSPF_ABR_DEFAULT; + ospf_schedule_abr_task(ospf); + } + + return CMD_SUCCESS; +} + +DEFUN (ospf_log_adjacency_changes, + ospf_log_adjacency_changes_cmd, + "log-adjacency-changes", + "Log changes in adjacency state\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES); + UNSET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL); + return CMD_SUCCESS; +} + +DEFUN (ospf_log_adjacency_changes_detail, + ospf_log_adjacency_changes_detail_cmd, + "log-adjacency-changes detail", + "Log changes in adjacency state\n" + "Log all state changes\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES); + SET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL); + return CMD_SUCCESS; +} + +DEFUN (no_ospf_log_adjacency_changes, + no_ospf_log_adjacency_changes_cmd, + "no log-adjacency-changes", + NO_STR + "Log changes in adjacency state\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + UNSET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL); + UNSET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES); + return CMD_SUCCESS; +} + +DEFUN (no_ospf_log_adjacency_changes_detail, + no_ospf_log_adjacency_changes_detail_cmd, + "no log-adjacency-changes detail", + NO_STR + "Log changes in adjacency state\n" + "Log all state changes\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + UNSET_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL); + return CMD_SUCCESS; +} + +DEFUN (ospf_compatible_rfc1583, + ospf_compatible_rfc1583_cmd, + "compatible rfc1583", + "OSPF compatibility list\n" + "compatible with RFC 1583\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + if (!CHECK_FLAG(ospf->config, OSPF_RFC1583_COMPATIBLE)) { + SET_FLAG(ospf->config, OSPF_RFC1583_COMPATIBLE); + ospf_spf_calculate_schedule(ospf, SPF_FLAG_CONFIG_CHANGE); + } + return CMD_SUCCESS; +} + +DEFUN (no_ospf_compatible_rfc1583, + no_ospf_compatible_rfc1583_cmd, + "no compatible rfc1583", + NO_STR + "OSPF compatibility list\n" + "compatible with RFC 1583\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + if (CHECK_FLAG(ospf->config, OSPF_RFC1583_COMPATIBLE)) { + UNSET_FLAG(ospf->config, OSPF_RFC1583_COMPATIBLE); + ospf_spf_calculate_schedule(ospf, SPF_FLAG_CONFIG_CHANGE); + } + return CMD_SUCCESS; +} + +ALIAS(ospf_compatible_rfc1583, ospf_rfc1583_flag_cmd, + "ospf rfc1583compatibility", + "OSPF specific commands\n" + "Enable the RFC1583Compatibility flag\n") + +ALIAS(no_ospf_compatible_rfc1583, no_ospf_rfc1583_flag_cmd, + "no ospf rfc1583compatibility", NO_STR + "OSPF specific commands\n" + "Disable the RFC1583Compatibility flag\n") + +static void ospf_table_reinstall_routes(struct ospf *ospf, + struct route_table *rt) +{ + struct route_node *rn; + + if (!rt) + return; + + for (rn = route_top(rt); rn; rn = route_next(rn)) { + struct ospf_route *or; + + or = rn->info; + if (!or) + continue; + + if (or->type == OSPF_DESTINATION_NETWORK) + ospf_zebra_add(ospf, (struct prefix_ipv4 *)&rn->p, or); + else if (or->type == OSPF_DESTINATION_DISCARD) + ospf_zebra_add_discard(ospf, + (struct prefix_ipv4 *)&rn->p); + } +} + +static void ospf_reinstall_routes(struct ospf *ospf) +{ + ospf_table_reinstall_routes(ospf, ospf->new_table); + ospf_table_reinstall_routes(ospf, ospf->new_external_route); +} + +DEFPY (ospf_send_extra_data, + ospf_send_extra_data_cmd, + "[no] ospf send-extra-data zebra", + NO_STR + OSPF_STR + "Extra data to Zebra for display/use\n" + "To zebra\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + if (no && CHECK_FLAG(ospf->config, OSPF_SEND_EXTRA_DATA_TO_ZEBRA)) { + UNSET_FLAG(ospf->config, OSPF_SEND_EXTRA_DATA_TO_ZEBRA); + ospf_reinstall_routes(ospf); + } else if (!CHECK_FLAG(ospf->config, OSPF_SEND_EXTRA_DATA_TO_ZEBRA)) { + SET_FLAG(ospf->config, OSPF_SEND_EXTRA_DATA_TO_ZEBRA); + ospf_reinstall_routes(ospf); + } + + return CMD_SUCCESS; +} + +static int ospf_timers_spf_set(struct vty *vty, unsigned int delay, + unsigned int hold, unsigned int max) +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf->spf_delay = delay; + ospf->spf_holdtime = hold; + ospf->spf_max_holdtime = max; + + return CMD_SUCCESS; +} + +DEFUN (ospf_timers_min_ls_interval, + ospf_timers_min_ls_interval_cmd, + "timers throttle lsa all (0-5000)", + "Adjust routing timers\n" + "Throttling adaptive timer\n" + "LSA delay between transmissions\n" + "All LSA types\n" + "Delay (msec) between sending LSAs\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_number = 4; + unsigned int interval; + + if (argc < 5) { + vty_out(vty, "Insufficient arguments\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + interval = strtoul(argv[idx_number]->arg, NULL, 10); + + ospf->min_ls_interval = interval; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_timers_min_ls_interval, + no_ospf_timers_min_ls_interval_cmd, + "no timers throttle lsa all [(0-5000)]", + NO_STR + "Adjust routing timers\n" + "Throttling adaptive timer\n" + "LSA delay between transmissions\n" + "All LSA types\n" + "Delay (msec) between sending LSAs\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + ospf->min_ls_interval = OSPF_MIN_LS_INTERVAL; + + return CMD_SUCCESS; +} + +DEFUN (ospf_timers_throttle_spf, + ospf_timers_throttle_spf_cmd, + "timers throttle spf (0-600000) (0-600000) (0-600000)", + "Adjust routing timers\n" + "Throttling adaptive timer\n" + "OSPF SPF timers\n" + "Delay (msec) from first change received till SPF calculation\n" + "Initial hold time (msec) between consecutive SPF calculations\n" + "Maximum hold time (msec)\n") +{ + int idx_number = 3; + int idx_number_2 = 4; + int idx_number_3 = 5; + unsigned int delay, hold, max; + + if (argc < 6) { + vty_out(vty, "Insufficient arguments\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + delay = strtoul(argv[idx_number]->arg, NULL, 10); + hold = strtoul(argv[idx_number_2]->arg, NULL, 10); + max = strtoul(argv[idx_number_3]->arg, NULL, 10); + + return ospf_timers_spf_set(vty, delay, hold, max); +} + +DEFUN (no_ospf_timers_throttle_spf, + no_ospf_timers_throttle_spf_cmd, + "no timers throttle spf [(0-600000)(0-600000)(0-600000)]", + NO_STR + "Adjust routing timers\n" + "Throttling adaptive timer\n" + "OSPF SPF timers\n" + "Delay (msec) from first change received till SPF calculation\n" + "Initial hold time (msec) between consecutive SPF calculations\n" + "Maximum hold time (msec)\n") +{ + return ospf_timers_spf_set(vty, OSPF_SPF_DELAY_DEFAULT, + OSPF_SPF_HOLDTIME_DEFAULT, + OSPF_SPF_MAX_HOLDTIME_DEFAULT); +} + + +DEFUN (ospf_timers_lsa_min_arrival, + ospf_timers_lsa_min_arrival_cmd, + "timers lsa min-arrival (0-600000)", + "Adjust routing timers\n" + "OSPF LSA timers\n" + "Minimum delay in receiving new version of a LSA\n" + "Delay in milliseconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + ospf->min_ls_arrival = strtoul(argv[argc - 1]->arg, NULL, 10); + return CMD_SUCCESS; +} + +DEFUN (no_ospf_timers_lsa_min_arrival, + no_ospf_timers_lsa_min_arrival_cmd, + "no timers lsa min-arrival [(0-600000)]", + NO_STR + "Adjust routing timers\n" + "OSPF LSA timers\n" + "Minimum delay in receiving new version of a LSA\n" + "Delay in milliseconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + unsigned int minarrival; + + if (argc > 4) { + minarrival = strtoul(argv[argc - 1]->arg, NULL, 10); + + if (ospf->min_ls_arrival != minarrival + || minarrival == OSPF_MIN_LS_ARRIVAL) + return CMD_SUCCESS; + } + + ospf->min_ls_arrival = OSPF_MIN_LS_ARRIVAL; + + return CMD_SUCCESS; +} + +DEFUN (ospf_neighbor, + ospf_neighbor_cmd, + "neighbor A.B.C.D [priority (0-255) [poll-interval (1-65535)]]", + NEIGHBOR_STR + "Neighbor IP address\n" + "Neighbor Priority\n" + "Priority\n" + "Dead Neighbor Polling interval\n" + "Seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4 = 1; + int idx_pri = 3; + int idx_poll = 5; + struct in_addr nbr_addr; + unsigned int priority = OSPF_NEIGHBOR_PRIORITY_DEFAULT; + unsigned int interval = OSPF_POLL_INTERVAL_DEFAULT; + + if (!inet_aton(argv[idx_ipv4]->arg, &nbr_addr)) { + vty_out(vty, "Please specify Neighbor ID by A.B.C.D\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (argc > 2) + priority = strtoul(argv[idx_pri]->arg, NULL, 10); + + if (argc > 4) + interval = strtoul(argv[idx_poll]->arg, NULL, 10); + + ospf_nbr_nbma_set(ospf, nbr_addr); + + if (argc > 2) + ospf_nbr_nbma_priority_set(ospf, nbr_addr, priority); + + if (argc > 4) + ospf_nbr_nbma_poll_interval_set(ospf, nbr_addr, interval); + + return CMD_SUCCESS; +} + +DEFUN (ospf_neighbor_poll_interval, + ospf_neighbor_poll_interval_cmd, + "neighbor A.B.C.D poll-interval (1-65535) [priority (0-255)]", + NEIGHBOR_STR + "Neighbor IP address\n" + "Dead Neighbor Polling interval\n" + "Seconds\n" + "OSPF priority of non-broadcast neighbor\n" + "Priority\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4 = 1; + int idx_poll = 3; + int idx_pri = 5; + struct in_addr nbr_addr; + unsigned int priority; + unsigned int interval; + + if (!inet_aton(argv[idx_ipv4]->arg, &nbr_addr)) { + vty_out(vty, "Please specify Neighbor ID by A.B.C.D\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + interval = strtoul(argv[idx_poll]->arg, NULL, 10); + + priority = argc > 4 ? strtoul(argv[idx_pri]->arg, NULL, 10) + : OSPF_NEIGHBOR_PRIORITY_DEFAULT; + + ospf_nbr_nbma_set(ospf, nbr_addr); + ospf_nbr_nbma_poll_interval_set(ospf, nbr_addr, interval); + + if (argc > 4) + ospf_nbr_nbma_priority_set(ospf, nbr_addr, priority); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_neighbor, + no_ospf_neighbor_cmd, + "no neighbor A.B.C.D [priority (0-255) [poll-interval (1-65525)]]", + NO_STR + NEIGHBOR_STR + "Neighbor IP address\n" + "Neighbor Priority\n" + "Priority\n" + "Dead Neighbor Polling interval\n" + "Seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4 = 2; + struct in_addr nbr_addr; + + if (!inet_aton(argv[idx_ipv4]->arg, &nbr_addr)) { + vty_out(vty, "Please specify Neighbor ID by A.B.C.D\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + (void)ospf_nbr_nbma_unset(ospf, nbr_addr); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_neighbor_poll, + no_ospf_neighbor_poll_cmd, + "no neighbor A.B.C.D poll-interval (1-65535) [priority (0-255)]", + NO_STR + NEIGHBOR_STR + "Neighbor IP address\n" + "Dead Neighbor Polling interval\n" + "Seconds\n" + "Neighbor Priority\n" + "Priority\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ipv4 = 2; + struct in_addr nbr_addr; + + if (!inet_aton(argv[idx_ipv4]->arg, &nbr_addr)) { + vty_out(vty, "Please specify Neighbor ID by A.B.C.D\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + (void)ospf_nbr_nbma_unset(ospf, nbr_addr); + + return CMD_SUCCESS; +} + +DEFUN (ospf_refresh_timer, + ospf_refresh_timer_cmd, + "refresh timer (10-1800)", + "Adjust refresh parameters\n" + "Set refresh timer\n" + "Timer value in seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_number = 2; + unsigned int interval; + + interval = strtoul(argv[idx_number]->arg, NULL, 10); + interval = (interval / OSPF_LSA_REFRESHER_GRANULARITY) + * OSPF_LSA_REFRESHER_GRANULARITY; + + ospf_timers_refresh_set(ospf, interval); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_refresh_timer, + no_ospf_refresh_timer_val_cmd, + "no refresh timer [(10-1800)]", + NO_STR + "Adjust refresh parameters\n" + "Unset refresh timer\n" + "Timer value in seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_number = 3; + unsigned int interval; + + if (argc == 1) { + interval = strtoul(argv[idx_number]->arg, NULL, 10); + + if (ospf->lsa_refresh_interval != interval + || interval == OSPF_LSA_REFRESH_INTERVAL_DEFAULT) + return CMD_SUCCESS; + } + + ospf_timers_refresh_unset(ospf); + + return CMD_SUCCESS; +} + + +DEFUN (ospf_auto_cost_reference_bandwidth, + ospf_auto_cost_reference_bandwidth_cmd, + "auto-cost reference-bandwidth (1-4294967)", + "Calculate OSPF interface cost according to bandwidth\n" + "Use reference bandwidth method to assign OSPF cost\n" + "The reference bandwidth in terms of Mbits per second\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + int idx_number = 2; + uint32_t refbw; + struct interface *ifp; + + refbw = strtol(argv[idx_number]->arg, NULL, 10); + if (refbw < 1 || refbw > 4294967) { + vty_out(vty, "reference-bandwidth value is invalid\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* If reference bandwidth is changed. */ + if ((refbw) == ospf->ref_bandwidth) + return CMD_SUCCESS; + + ospf->ref_bandwidth = refbw; + FOR_ALL_INTERFACES (vrf, ifp) + ospf_if_recalculate_output_cost(ifp); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_auto_cost_reference_bandwidth, + no_ospf_auto_cost_reference_bandwidth_cmd, + "no auto-cost reference-bandwidth [(1-4294967)]", + NO_STR + "Calculate OSPF interface cost according to bandwidth\n" + "Use reference bandwidth method to assign OSPF cost\n" + "The reference bandwidth in terms of Mbits per second\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + struct interface *ifp; + + if (ospf->ref_bandwidth == OSPF_DEFAULT_REF_BANDWIDTH) + return CMD_SUCCESS; + + ospf->ref_bandwidth = OSPF_DEFAULT_REF_BANDWIDTH; + vty_out(vty, "%% OSPF: Reference bandwidth is changed.\n"); + vty_out(vty, + " Please ensure reference bandwidth is consistent across all routers\n"); + + FOR_ALL_INTERFACES (vrf, ifp) + ospf_if_recalculate_output_cost(ifp); + + return CMD_SUCCESS; +} + +DEFUN (ospf_write_multiplier, + ospf_write_multiplier_cmd, + "ospf write-multiplier (1-100)", + "OSPF specific commands\n" + "Write multiplier\n" + "Maximum number of interface serviced per write\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_number; + uint32_t write_oi_count; + + if (argc == 3) + idx_number = 2; + else + idx_number = 1; + + write_oi_count = strtol(argv[idx_number]->arg, NULL, 10); + if (write_oi_count < 1 || write_oi_count > 100) { + vty_out(vty, "write-multiplier value is invalid\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ospf->write_oi_count = write_oi_count; + return CMD_SUCCESS; +} + +ALIAS(ospf_write_multiplier, write_multiplier_cmd, "write-multiplier (1-100)", + "Write multiplier\n" + "Maximum number of interface serviced per write\n") + +DEFUN (no_ospf_write_multiplier, + no_ospf_write_multiplier_cmd, + "no ospf write-multiplier (1-100)", + NO_STR + "OSPF specific commands\n" + "Write multiplier\n" + "Maximum number of interface serviced per write\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf->write_oi_count = OSPF_WRITE_INTERFACE_COUNT_DEFAULT; + return CMD_SUCCESS; +} + +ALIAS(no_ospf_write_multiplier, no_write_multiplier_cmd, + "no write-multiplier (1-100)", NO_STR + "Write multiplier\n" + "Maximum number of interface serviced per write\n") + +DEFUN(ospf_ti_lfa, ospf_ti_lfa_cmd, "fast-reroute ti-lfa [node-protection]", + "Fast Reroute for MPLS and IP resilience\n" + "Topology Independent LFA (Loop-Free Alternate)\n" + "TI-LFA node protection (default is link protection)\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf->ti_lfa_enabled = true; + + if (argc == 3) + ospf->ti_lfa_protection_type = OSPF_TI_LFA_NODE_PROTECTION; + else + ospf->ti_lfa_protection_type = OSPF_TI_LFA_LINK_PROTECTION; + + ospf_spf_calculate_schedule(ospf, SPF_FLAG_CONFIG_CHANGE); + + return CMD_SUCCESS; +} + +DEFUN(no_ospf_ti_lfa, no_ospf_ti_lfa_cmd, + "no fast-reroute ti-lfa [node-protection]", + NO_STR + "Fast Reroute for MPLS and IP resilience\n" + "Topology Independent LFA (Loop-Free Alternate)\n" + "TI-LFA node protection (default is link protection)\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf->ti_lfa_enabled = false; + + ospf->ti_lfa_protection_type = OSPF_TI_LFA_UNDEFINED_PROTECTION; + + ospf_spf_calculate_schedule(ospf, SPF_FLAG_CONFIG_CHANGE); + + return CMD_SUCCESS; +} + +static void ospf_maxpath_set(struct vty *vty, struct ospf *ospf, uint16_t paths) +{ + if (ospf->max_multipath == paths) + return; + + ospf->max_multipath = paths; + + /* Send deletion notification to zebra to delete all + * ospf specific routes and reinitiat SPF to reflect + * the new max multipath. + */ + ospf_restart_spf(ospf); +} + +/* Ospf Maximum multiple paths config support */ +DEFUN (ospf_max_multipath, + ospf_max_multipath_cmd, + "maximum-paths " CMD_RANGE_STR(1, MULTIPATH_NUM), + "Max no of multiple paths for ECMP support\n" + "Number of paths\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_number = 1; + uint16_t maxpaths; + + maxpaths = strtol(argv[idx_number]->arg, NULL, 10); + + ospf_maxpath_set(vty, ospf, maxpaths); + return CMD_SUCCESS; +} + +DEFUN (no_ospf_max_multipath, + no_ospf_max_multipath_cmd, + "no maximum-paths", + NO_STR + "Max no of multiple paths for ECMP support\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + uint16_t maxpaths = MULTIPATH_NUM; + + ospf_maxpath_set(vty, ospf, maxpaths); + return CMD_SUCCESS; +} + +static const char *const ospf_abr_type_descr_str[] = { + "Unknown", "Standard (RFC2328)", "Alternative IBM", + "Alternative Cisco", "Alternative Shortcut" +}; + +static const char *const ospf_shortcut_mode_descr_str[] = { + "Default", "Enabled", "Disabled" +}; + +static void show_ip_ospf_area(struct vty *vty, struct ospf_area *area, + json_object *json_areas, bool use_json) +{ + json_object *json_area = NULL; + char buf[PREFIX_STRLEN]; + + if (use_json) + json_area = json_object_new_object(); + + /* Show Area ID. */ + if (!use_json) + vty_out(vty, " Area ID: %pI4", &area->area_id); + + /* Show Area type/mode. */ + if (OSPF_IS_AREA_BACKBONE(area)) { + if (use_json) + json_object_boolean_true_add(json_area, "backbone"); + else + vty_out(vty, " (Backbone)\n"); + } else { + if (use_json) { + if (area->external_routing == OSPF_AREA_STUB) { + if (area->no_summary) + json_object_boolean_true_add( + json_area, "stubNoSummary"); + if (area->shortcut_configured) + json_object_boolean_true_add( + json_area, "stubShortcut"); + } else if (area->external_routing == OSPF_AREA_NSSA) { + if (area->no_summary) + json_object_boolean_true_add( + json_area, "nssaNoSummary"); + if (area->shortcut_configured) + json_object_boolean_true_add( + json_area, "nssaShortcut"); + } + + json_object_string_add( + json_area, "shortcuttingMode", + ospf_shortcut_mode_descr_str + [area->shortcut_configured]); + if (area->shortcut_capability) + json_object_boolean_true_add(json_area, + "sBitConcensus"); + } else { + if (area->external_routing == OSPF_AREA_STUB) + vty_out(vty, " (Stub%s%s)", + area->no_summary ? ", no summary" : "", + area->shortcut_configured ? "; " : ""); + else if (area->external_routing == OSPF_AREA_NSSA) + vty_out(vty, " (NSSA%s%s)", + area->no_summary ? ", no summary" : "", + area->shortcut_configured ? "; " : ""); + + vty_out(vty, "\n"); + vty_out(vty, " Shortcutting mode: %s", + ospf_shortcut_mode_descr_str + [area->shortcut_configured]); + vty_out(vty, ", S-bit consensus: %s\n", + area->shortcut_capability ? "ok" : "no"); + } + } + + /* Show number of interfaces */ + if (use_json) { + json_object_int_add(json_area, "areaIfTotalCounter", + listcount(area->oiflist)); + json_object_int_add(json_area, "areaIfActiveCounter", + area->act_ints); + } else + vty_out(vty, + " Number of interfaces in this area: Total: %d, Active: %d\n", + listcount(area->oiflist), area->act_ints); + + if (area->external_routing == OSPF_AREA_NSSA) { + if (use_json) { + json_object_boolean_true_add(json_area, "nssa"); + if (!IS_OSPF_ABR(area->ospf)) + json_object_boolean_false_add(json_area, "abr"); + else if (area->NSSATranslatorState) { + json_object_boolean_true_add(json_area, "abr"); + if (area->NSSATranslatorRole + == OSPF_NSSA_ROLE_CANDIDATE) + json_object_boolean_true_add( + json_area, + "nssaTranslatorElected"); + else if (area->NSSATranslatorRole + == OSPF_NSSA_ROLE_ALWAYS) + json_object_boolean_true_add( + json_area, + "nssaTranslatorAlways"); + else + json_object_boolean_true_add( + json_area, + "nssaTranslatorNever"); + } else { + json_object_boolean_true_add(json_area, "abr"); + if (area->NSSATranslatorRole + == OSPF_NSSA_ROLE_CANDIDATE) + json_object_boolean_false_add( + json_area, + "nssaTranslatorElected"); + else + json_object_boolean_true_add( + json_area, + "nssaTranslatorNever"); + } + } else { + vty_out(vty, + " It is an NSSA configuration.\n Elected NSSA/ABR performs type-7/type-5 LSA translation.\n"); + if (!IS_OSPF_ABR(area->ospf)) + vty_out(vty, + " It is not ABR, therefore not Translator.\n"); + else if (area->NSSATranslatorState) { + vty_out(vty, " We are an ABR and "); + if (area->NSSATranslatorRole + == OSPF_NSSA_ROLE_CANDIDATE) + vty_out(vty, + "the NSSA Elected Translator.\n"); + else if (area->NSSATranslatorRole + == OSPF_NSSA_ROLE_ALWAYS) + vty_out(vty, + "always an NSSA Translator.\n"); + else + vty_out(vty, + "never an NSSA Translator.\n"); + } else { + vty_out(vty, " We are an ABR, but "); + if (area->NSSATranslatorRole + == OSPF_NSSA_ROLE_CANDIDATE) + vty_out(vty, + "not the NSSA Elected Translator.\n"); + else + vty_out(vty, + "never an NSSA Translator.\n"); + } + } + } + + /* Stub-router state for this area */ + if (CHECK_FLAG(area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED)) { + char timebuf[OSPF_TIME_DUMP_SIZE]; + + if (use_json) { + json_object_boolean_true_add( + json_area, "originStubMaxDistRouterLsa"); + if (CHECK_FLAG(area->stub_router_state, + OSPF_AREA_ADMIN_STUB_ROUTED)) + json_object_boolean_true_add( + json_area, "indefiniteActiveAdmin"); + if (area->t_stub_router) { + long time_store; + time_store = + monotime_until( + &area->t_stub_router->u.sands, + NULL) + / 1000LL; + json_object_int_add( + json_area, + "activeStartupRemainderMsecs", + time_store); + } + } else { + vty_out(vty, + " Originating stub / maximum-distance Router-LSA\n"); + if (CHECK_FLAG(area->stub_router_state, + OSPF_AREA_ADMIN_STUB_ROUTED)) + vty_out(vty, + " Administratively activated (indefinitely)\n"); + if (area->t_stub_router) + vty_out(vty, + " Active from startup, %s remaining\n", + ospf_timer_dump(area->t_stub_router, + timebuf, + sizeof(timebuf))); + } + } + + if (use_json) { + /* Show number of fully adjacent neighbors. */ + json_object_int_add(json_area, "nbrFullAdjacentCounter", + area->full_nbrs); + + /* Show authentication type. */ + if (area->auth_type == OSPF_AUTH_NULL) + json_object_string_add(json_area, "authentication", + "authenticationNone"); + else if (area->auth_type == OSPF_AUTH_SIMPLE) + json_object_string_add(json_area, "authentication", + "authenticationSimplePassword"); + else if (area->auth_type == OSPF_AUTH_CRYPTOGRAPHIC) + json_object_string_add(json_area, "authentication", + "authenticationMessageDigest"); + + if (!OSPF_IS_AREA_BACKBONE(area)) + json_object_int_add(json_area, + "virtualAdjacenciesPassingCounter", + area->full_vls); + + /* Show SPF calculation times. */ + json_object_int_add(json_area, "spfExecutedCounter", + area->spf_calculation); + json_object_int_add(json_area, "lsaNumber", area->lsdb->total); + json_object_int_add( + json_area, "lsaRouterNumber", + ospf_lsdb_count(area->lsdb, OSPF_ROUTER_LSA)); + json_object_int_add( + json_area, "lsaRouterChecksum", + ospf_lsdb_checksum(area->lsdb, OSPF_ROUTER_LSA)); + json_object_int_add( + json_area, "lsaNetworkNumber", + ospf_lsdb_count(area->lsdb, OSPF_NETWORK_LSA)); + json_object_int_add( + json_area, "lsaNetworkChecksum", + ospf_lsdb_checksum(area->lsdb, OSPF_NETWORK_LSA)); + json_object_int_add( + json_area, "lsaSummaryNumber", + ospf_lsdb_count(area->lsdb, OSPF_SUMMARY_LSA)); + json_object_int_add( + json_area, "lsaSummaryChecksum", + ospf_lsdb_checksum(area->lsdb, OSPF_SUMMARY_LSA)); + json_object_int_add( + json_area, "lsaAsbrNumber", + ospf_lsdb_count(area->lsdb, OSPF_ASBR_SUMMARY_LSA)); + json_object_int_add( + json_area, "lsaAsbrChecksum", + ospf_lsdb_checksum(area->lsdb, OSPF_ASBR_SUMMARY_LSA)); + json_object_int_add( + json_area, "lsaNssaNumber", + ospf_lsdb_count(area->lsdb, OSPF_AS_NSSA_LSA)); + json_object_int_add( + json_area, "lsaNssaChecksum", + ospf_lsdb_checksum(area->lsdb, OSPF_AS_NSSA_LSA)); + } else { + /* Show number of fully adjacent neighbors. */ + vty_out(vty, + " Number of fully adjacent neighbors in this area: %d\n", + area->full_nbrs); + + /* Show authentication type. */ + vty_out(vty, " Area has "); + if (area->auth_type == OSPF_AUTH_NULL) + vty_out(vty, "no authentication\n"); + else if (area->auth_type == OSPF_AUTH_SIMPLE) + vty_out(vty, "simple password authentication\n"); + else if (area->auth_type == OSPF_AUTH_CRYPTOGRAPHIC) + vty_out(vty, "message digest authentication\n"); + + if (!OSPF_IS_AREA_BACKBONE(area)) + vty_out(vty, + " Number of full virtual adjacencies going through this area: %d\n", + area->full_vls); + + /* Show SPF calculation times. */ + vty_out(vty, " SPF algorithm executed %d times\n", + area->spf_calculation); + + /* Show number of LSA. */ + vty_out(vty, " Number of LSA %ld\n", area->lsdb->total); + vty_out(vty, + " Number of router LSA %ld. Checksum Sum 0x%08x\n", + ospf_lsdb_count(area->lsdb, OSPF_ROUTER_LSA), + ospf_lsdb_checksum(area->lsdb, OSPF_ROUTER_LSA)); + vty_out(vty, + " Number of network LSA %ld. Checksum Sum 0x%08x\n", + ospf_lsdb_count(area->lsdb, OSPF_NETWORK_LSA), + ospf_lsdb_checksum(area->lsdb, OSPF_NETWORK_LSA)); + vty_out(vty, + " Number of summary LSA %ld. Checksum Sum 0x%08x\n", + ospf_lsdb_count(area->lsdb, OSPF_SUMMARY_LSA), + ospf_lsdb_checksum(area->lsdb, OSPF_SUMMARY_LSA)); + vty_out(vty, + " Number of ASBR summary LSA %ld. Checksum Sum 0x%08x\n", + ospf_lsdb_count(area->lsdb, OSPF_ASBR_SUMMARY_LSA), + ospf_lsdb_checksum(area->lsdb, OSPF_ASBR_SUMMARY_LSA)); + vty_out(vty, " Number of NSSA LSA %ld. Checksum Sum 0x%08x\n", + ospf_lsdb_count(area->lsdb, OSPF_AS_NSSA_LSA), + ospf_lsdb_checksum(area->lsdb, OSPF_AS_NSSA_LSA)); + } + + if (use_json) { + json_object_int_add( + json_area, "lsaOpaqueLinkNumber", + ospf_lsdb_count(area->lsdb, OSPF_OPAQUE_LINK_LSA)); + json_object_int_add( + json_area, "lsaOpaqueLinkChecksum", + ospf_lsdb_checksum(area->lsdb, OSPF_OPAQUE_LINK_LSA)); + json_object_int_add( + json_area, "lsaOpaqueAreaNumber", + ospf_lsdb_count(area->lsdb, OSPF_OPAQUE_AREA_LSA)); + json_object_int_add( + json_area, "lsaOpaqueAreaChecksum", + ospf_lsdb_checksum(area->lsdb, OSPF_OPAQUE_AREA_LSA)); + } else { + vty_out(vty, + " Number of opaque link LSA %ld. Checksum Sum 0x%08x\n", + ospf_lsdb_count(area->lsdb, OSPF_OPAQUE_LINK_LSA), + ospf_lsdb_checksum(area->lsdb, OSPF_OPAQUE_LINK_LSA)); + vty_out(vty, + " Number of opaque area LSA %ld. Checksum Sum 0x%08x\n", + ospf_lsdb_count(area->lsdb, OSPF_OPAQUE_AREA_LSA), + ospf_lsdb_checksum(area->lsdb, OSPF_OPAQUE_AREA_LSA)); + } + + if (area->fr_info.configured) { + if (use_json) + json_object_string_add(json_area, "areaFloodReduction", + "configured"); + else + vty_out(vty, " Flood Reduction is configured.\n"); + } + + if (area->fr_info.enabled) { + if (use_json) { + json_object_boolean_true_add( + json_area, "areaFloodReductionEnabled"); + if (area->fr_info.router_lsas_recv_dc_bit) + json_object_boolean_true_add( + json_area, "lsasRecvDCbitSet"); + if (area->fr_info.area_ind_lsa_recvd) + json_object_string_add(json_area, + "areaIndicationLsaRecv", + "received"); + if (area->fr_info.indication_lsa_self) + json_object_string_addf( + json_area, "areaIndicationLsa", "%pI4", + &area->fr_info.indication_lsa_self->data + ->id); + } else { + vty_out(vty, " Flood Reduction is enabled.\n"); + vty_out(vty, " No of LSAs rcv'd with DC bit set %d\n", + area->fr_info.router_lsas_recv_dc_bit); + if (area->fr_info.area_ind_lsa_recvd) + vty_out(vty, " Ind LSA by other abr.\n"); + if (area->fr_info.indication_lsa_self) + vty_out(vty, " Ind LSA generated %pI4\n", + &area->fr_info.indication_lsa_self->data + ->id); + } + } + + if (use_json) + json_object_object_add(json_areas, + inet_ntop(AF_INET, &area->area_id, + buf, sizeof(buf)), + json_area); + else + vty_out(vty, "\n"); +} + +static int show_ip_ospf_common(struct vty *vty, struct ospf *ospf, + json_object *json, uint8_t use_vrf) +{ + struct listnode *node, *nnode; + struct ospf_area *area; + struct timeval result; + char timebuf[OSPF_TIME_DUMP_SIZE]; + json_object *json_vrf = NULL; + json_object *json_areas = NULL; + + if (json) { + if (use_vrf) + json_vrf = json_object_new_object(); + else + json_vrf = json; + json_areas = json_object_new_object(); + } + + if (ospf->instance) { + if (json) { + json_object_int_add(json, "ospfInstance", + ospf->instance); + } else { + vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); + } + } + + ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf); + + /* Show Router ID. */ + if (json) { + json_object_string_addf(json_vrf, "routerId", "%pI4", + &ospf->router_id); + } else { + vty_out(vty, " OSPF Routing Process, Router ID: %pI4\n", + &ospf->router_id); + } + + /* Graceful shutdown */ + if (ospf->t_deferred_shutdown) { + if (json) { + long time_store; + time_store = + monotime_until( + &ospf->t_deferred_shutdown->u.sands, + NULL) + / 1000LL; + json_object_int_add(json_vrf, "deferredShutdownMsecs", + time_store); + } else { + vty_out(vty, + " Deferred shutdown in progress, %s remaining\n", + ospf_timer_dump(ospf->t_deferred_shutdown, + timebuf, sizeof(timebuf))); + } + } + + /* Show capability. */ + if (json) { + json_object_boolean_true_add(json_vrf, "tosRoutesOnly"); + json_object_boolean_true_add(json_vrf, "rfc2328Conform"); + if (CHECK_FLAG(ospf->config, OSPF_RFC1583_COMPATIBLE)) { + json_object_boolean_true_add(json_vrf, + "rfc1583Compatibility"); + } + } else { + vty_out(vty, " Supports only single TOS (TOS0) routes\n"); + vty_out(vty, " This implementation conforms to RFC2328\n"); + vty_out(vty, " RFC1583Compatibility flag is %s\n", + CHECK_FLAG(ospf->config, OSPF_RFC1583_COMPATIBLE) + ? "enabled" + : "disabled"); + } + + if (json) { + if (CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) { + json_object_boolean_true_add(json_vrf, "opaqueCapable"); + } + } else { + vty_out(vty, " OpaqueCapability flag is %s\n", + CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE) + ? "enabled" + : "disabled"); + } + + /* Show stub-router configuration */ + if (ospf->stub_router_startup_time != OSPF_STUB_ROUTER_UNCONFIGURED + || ospf->stub_router_shutdown_time + != OSPF_STUB_ROUTER_UNCONFIGURED) { + if (json) { + json_object_boolean_true_add(json_vrf, + "stubAdvertisement"); + if (ospf->stub_router_startup_time + != OSPF_STUB_ROUTER_UNCONFIGURED) + json_object_int_add( + json_vrf, "postStartEnabledSecs", + ospf->stub_router_startup_time); + if (ospf->stub_router_shutdown_time + != OSPF_STUB_ROUTER_UNCONFIGURED) + json_object_int_add( + json_vrf, "preShutdownEnabledSecs", + ospf->stub_router_shutdown_time); + } else { + vty_out(vty, + " Stub router advertisement is configured\n"); + if (ospf->stub_router_startup_time + != OSPF_STUB_ROUTER_UNCONFIGURED) + vty_out(vty, + " Enabled for %us after start-up\n", + ospf->stub_router_startup_time); + if (ospf->stub_router_shutdown_time + != OSPF_STUB_ROUTER_UNCONFIGURED) + vty_out(vty, + " Enabled for %us prior to full shutdown\n", + ospf->stub_router_shutdown_time); + } + } + + /* Show SPF timers. */ + if (json) { + json_object_int_add(json_vrf, "spfScheduleDelayMsecs", + ospf->spf_delay); + json_object_int_add(json_vrf, "holdtimeMinMsecs", + ospf->spf_holdtime); + json_object_int_add(json_vrf, "holdtimeMaxMsecs", + ospf->spf_max_holdtime); + json_object_int_add(json_vrf, "holdtimeMultplier", + ospf->spf_hold_multiplier); + } else { + vty_out(vty, + " Initial SPF scheduling delay %d millisec(s)\n" + " Minimum hold time between consecutive SPFs %d millisec(s)\n" + " Maximum hold time between consecutive SPFs %d millisec(s)\n" + " Hold time multiplier is currently %d\n", + ospf->spf_delay, ospf->spf_holdtime, + ospf->spf_max_holdtime, ospf->spf_hold_multiplier); + } + + if (json) { + if (ospf->ts_spf.tv_sec || ospf->ts_spf.tv_usec) { + long time_store = 0; + + time_store = + monotime_since(&ospf->ts_spf, NULL) / 1000LL; + json_object_int_add(json_vrf, "spfLastExecutedMsecs", + time_store); + + time_store = (1000 * ospf->ts_spf_duration.tv_sec) + + (ospf->ts_spf_duration.tv_usec / 1000); + json_object_int_add(json_vrf, "spfLastDurationMsecs", + time_store); + } else + json_object_boolean_true_add(json_vrf, "spfHasNotRun"); + } else { + vty_out(vty, " SPF algorithm "); + if (ospf->ts_spf.tv_sec || ospf->ts_spf.tv_usec) { + monotime_since(&ospf->ts_spf, &result); + vty_out(vty, "last executed %s ago\n", + ospf_timeval_dump(&result, timebuf, + sizeof(timebuf))); + vty_out(vty, " Last SPF duration %s\n", + ospf_timeval_dump(&ospf->ts_spf_duration, + timebuf, sizeof(timebuf))); + } else + vty_out(vty, "has not been run\n"); + } + + if (json) { + if (ospf->t_spf_calc) { + long time_store; + time_store = + monotime_until(&ospf->t_spf_calc->u.sands, NULL) + / 1000LL; + json_object_int_add(json_vrf, "spfTimerDueInMsecs", + time_store); + } + + json_object_int_add(json_vrf, "lsaMinIntervalMsecs", + ospf->min_ls_interval); + json_object_int_add(json_vrf, "lsaMinArrivalMsecs", + ospf->min_ls_arrival); + /* Show write multiplier values */ + json_object_int_add(json_vrf, "writeMultiplier", + ospf->write_oi_count); + /* Show refresh parameters. */ + json_object_int_add(json_vrf, "refreshTimerMsecs", + ospf->lsa_refresh_interval * 1000); + + /* show max multipath */ + json_object_int_add(json_vrf, "maximumPaths", + ospf->max_multipath); + + /* show administrative distance */ + json_object_int_add(json_vrf, "preference", + ospf->distance_all + ? ospf->distance_all + : ZEBRA_OSPF_DISTANCE_DEFAULT); + } else { + vty_out(vty, " SPF timer %s%s\n", + (ospf->t_spf_calc ? "due in " : "is "), + ospf_timer_dump(ospf->t_spf_calc, timebuf, + sizeof(timebuf))); + + vty_out(vty, " LSA minimum interval %d msecs\n", + ospf->min_ls_interval); + vty_out(vty, " LSA minimum arrival %d msecs\n", + ospf->min_ls_arrival); + + /* Show write multiplier values */ + vty_out(vty, " Write Multiplier set to %d \n", + ospf->write_oi_count); + + /* Show refresh parameters. */ + vty_out(vty, " Refresh timer %d secs\n", + ospf->lsa_refresh_interval); + + /* show max multipath */ + vty_out(vty, " Maximum multiple paths(ECMP) supported %d\n", + ospf->max_multipath); + + /* show administrative distance */ + vty_out(vty, " Administrative distance %u\n", + ospf->distance_all ? ospf->distance_all + : ZEBRA_OSPF_DISTANCE_DEFAULT); + } + + if (ospf->fr_configured) { + if (json) + json_object_string_add(json_vrf, "floodReduction", + "configured"); + else + vty_out(vty, " Flood Reduction is configured.\n"); + } + + /* Show ABR/ASBR flags. */ + if (CHECK_FLAG(ospf->flags, OSPF_FLAG_ABR)) { + if (json) + json_object_string_add( + json_vrf, "abrType", + ospf_abr_type_descr_str[ospf->abr_type]); + else + vty_out(vty, + " This router is an ABR, ABR type is: %s\n", + ospf_abr_type_descr_str[ospf->abr_type]); + } + if (CHECK_FLAG(ospf->flags, OSPF_FLAG_ASBR)) { + if (json) + json_object_string_add( + json_vrf, "asbrRouter", + "injectingExternalRoutingInformation"); + else + vty_out(vty, + " This router is an ASBR (injecting external routing information)\n"); + } + + /* Show Number of AS-external-LSAs. */ + if (json) { + json_object_int_add( + json_vrf, "lsaExternalCounter", + ospf_lsdb_count(ospf->lsdb, OSPF_AS_EXTERNAL_LSA)); + json_object_int_add( + json_vrf, "lsaExternalChecksum", + ospf_lsdb_checksum(ospf->lsdb, OSPF_AS_EXTERNAL_LSA)); + } else { + vty_out(vty, + " Number of external LSA %ld. Checksum Sum 0x%08x\n", + ospf_lsdb_count(ospf->lsdb, OSPF_AS_EXTERNAL_LSA), + ospf_lsdb_checksum(ospf->lsdb, OSPF_AS_EXTERNAL_LSA)); + } + + if (json) { + json_object_int_add( + json_vrf, "lsaAsopaqueCounter", + ospf_lsdb_count(ospf->lsdb, OSPF_OPAQUE_AS_LSA)); + json_object_int_add( + json_vrf, "lsaAsOpaqueChecksum", + ospf_lsdb_checksum(ospf->lsdb, OSPF_OPAQUE_AS_LSA)); + } else { + vty_out(vty, + " Number of opaque AS LSA %ld. Checksum Sum 0x%08x\n", + ospf_lsdb_count(ospf->lsdb, OSPF_OPAQUE_AS_LSA), + ospf_lsdb_checksum(ospf->lsdb, OSPF_OPAQUE_AS_LSA)); + } + + /* Show number of areas attached. */ + if (json) + json_object_int_add(json_vrf, "attachedAreaCounter", + listcount(ospf->areas)); + else + vty_out(vty, " Number of areas attached to this router: %d\n", + listcount(ospf->areas)); + + if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES)) { + if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) { + if (json) + json_object_boolean_true_add( + json_vrf, "adjacencyChangesLoggedAll"); + else + vty_out(vty, + " All adjacency changes are logged\n"); + } else { + if (json) + json_object_boolean_true_add( + json_vrf, "adjacencyChangesLogged"); + else + vty_out(vty, " Adjacency changes are logged\n"); + } + } + + /* show LDP-Sync status */ + ospf_ldp_sync_show_info(vty, ospf, json_vrf, json ? 1 : 0); + + /* Socket buffer sizes */ + if (json) { + if (ospf->recv_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE) + json_object_int_add(json_vrf, "recvSockBufsize", + ospf->recv_sock_bufsize); + if (ospf->send_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE) + json_object_int_add(json_vrf, "sendSockBufsize", + ospf->send_sock_bufsize); + } else { + if (ospf->recv_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE) + vty_out(vty, " Receive socket bufsize: %u\n", + ospf->recv_sock_bufsize); + if (ospf->send_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE) + vty_out(vty, " Send socket bufsize: %u\n", + ospf->send_sock_bufsize); + } + + /* Show each area status. */ + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) + show_ip_ospf_area(vty, area, json_areas, json ? 1 : 0); + + if (json) { + if (use_vrf) { + json_object_object_add(json_vrf, "areas", json_areas); + json_object_object_add(json, ospf_get_name(ospf), + json_vrf); + } else { + json_object_object_add(json, "areas", json_areas); + } + } else + vty_out(vty, "\n"); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf, + show_ip_ospf_cmd, + "show ip ospf [vrf ] [json]", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "All VRFs\n" + JSON_STR) +{ + struct ospf *ospf; + bool uj = use_json(argc, argv); + struct listnode *node = NULL; + char *vrf_name = NULL; + bool all_vrf = false; + int ret = CMD_SUCCESS; + int inst = 0; + int idx_vrf = 0; + json_object *json = NULL; + uint8_t use_vrf = 0; + + if (listcount(om->ospf) == 0) + return CMD_SUCCESS; + + OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + if (uj) + json = json_object_new_object(); + + /* vrf input is provided could be all or specific vrf*/ + if (vrf_name) { + bool ospf_output = false; + + use_vrf = 1; + + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + ospf_output = true; + ret = show_ip_ospf_common(vty, ospf, json, + use_vrf); + } + if (uj) + vty_json(vty, json); + else if (!ospf_output) + vty_out(vty, "%% OSPF is not enabled\n"); + return ret; + } + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + if ((ospf == NULL) || !ospf->oi_running) { + if (uj) + vty_json(vty, json); + else + vty_out(vty, + "%% OSPF is not enabled in vrf %s\n", + vrf_name); + + return CMD_SUCCESS; + } + } else { + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + /* Display default ospf (instance 0) info */ + if (ospf == NULL || !ospf->oi_running) { + if (uj) + vty_json(vty, json); + else + vty_out(vty, + "%% OSPF is not enabled in vrf default\n"); + + return CMD_SUCCESS; + } + } + + if (ospf) { + show_ip_ospf_common(vty, ospf, json, use_vrf); + if (uj) + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + } + + if (uj) + json_object_free(json); + + return ret; +} + +DEFUN (show_ip_ospf_instance, + show_ip_ospf_instance_cmd, + "show ip ospf (1-65535) [json]", + SHOW_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + JSON_STR) +{ + int idx_number = 3; + struct ospf *ospf; + unsigned short instance = 0; + bool uj = use_json(argc, argv); + int ret = CMD_SUCCESS; + json_object *json = NULL; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + ospf = ospf_lookup_instance(instance); + if (!ospf || !ospf->oi_running) + return CMD_SUCCESS; + + if (uj) + json = json_object_new_object(); + + ret = show_ip_ospf_common(vty, ospf, json, 0); + + if (uj) + vty_json(vty, json); + + return ret; +} + +static void ospf_interface_auth_show(struct vty *vty, struct ospf_interface *oi, + json_object *json, bool use_json) +{ + int auth_type; + + auth_type = OSPF_IF_PARAM(oi, auth_type); + + switch (auth_type) { + case OSPF_AUTH_NULL: + if (use_json) + json_object_string_add(json, "authentication", + "authenticationNone"); + else + vty_out(vty, " Authentication NULL is enabled\n"); + break; + case OSPF_AUTH_SIMPLE: { + if (use_json) + json_object_string_add(json, "authentication", + "authenticationSimplePassword"); + else + vty_out(vty, + " Simple password authentication enabled\n"); + break; + } + case OSPF_AUTH_CRYPTOGRAPHIC: { + struct crypt_key *ckey; + + if (OSPF_IF_PARAM(oi, keychain_name)) { + if (use_json) { + json_object_string_add(json, "authentication", + "authenticationKeyChain"); + json_object_string_add(json, "keychain", + OSPF_IF_PARAM(oi, keychain_name)); + } else { + vty_out(vty, + " Cryptographic authentication enabled\n"); + struct keychain *keychain = keychain_lookup(OSPF_IF_PARAM(oi, keychain_name)); + + if (keychain) { + struct key *key = key_lookup_for_send(keychain); + + if (key) { + vty_out(vty, " Sending SA: Key %u, Algorithm %s - key chain %s\n", + key->index, keychain_get_algo_name_by_id(key->hash_algo), + OSPF_IF_PARAM(oi, keychain_name)); + } + } + } + } else { + if (list_isempty(OSPF_IF_PARAM(oi, auth_crypt))) + return; + + ckey = listgetdata(listtail(OSPF_IF_PARAM(oi, auth_crypt))); + if (ckey) { + if (use_json) { + json_object_string_add(json, "authentication", + "authenticationMessageDigest"); + } else { + vty_out(vty, + " Cryptographic authentication enabled\n"); + vty_out(vty, " Algorithm:MD5\n"); + } + } + } + break; + } + default: + break; + } +} + +static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, + struct interface *ifp, + json_object *json_interface_sub, + bool use_json) +{ + int is_up; + struct ospf_neighbor *nbr; + struct route_node *rn; + uint32_t bandwidth = ifp->bandwidth ? ifp->bandwidth : ifp->speed; + struct ospf_if_params *params; + json_object *json_ois = NULL; + json_object *json_oi = NULL; + + /* Is interface up? */ + if (use_json) { + is_up = if_is_operative(ifp); + if (is_up) + json_object_boolean_true_add(json_interface_sub, + "ifUp"); + else + json_object_boolean_false_add(json_interface_sub, + "ifDown"); + + json_object_int_add(json_interface_sub, "ifIndex", + ifp->ifindex); + json_object_int_add(json_interface_sub, "mtuBytes", ifp->mtu); + json_object_int_add(json_interface_sub, "bandwidthMbit", + bandwidth); + json_object_string_add(json_interface_sub, "ifFlags", + if_flag_dump(ifp->flags)); + } else { + vty_out(vty, "%s is %s\n", ifp->name, + ((is_up = if_is_operative(ifp)) ? "up" : "down")); + vty_out(vty, " ifindex %u, MTU %u bytes, BW %u Mbit %s\n", + ifp->ifindex, ifp->mtu, bandwidth, + if_flag_dump(ifp->flags)); + } + + /* Is interface OSPF enabled? */ + if (use_json) { + if (ospf_oi_count(ifp) == 0) { + json_object_boolean_false_add(json_interface_sub, + "ospfEnabled"); + return; + } else if (!is_up) { + json_object_boolean_false_add(json_interface_sub, + "ospfRunning"); + return; + } else + json_object_boolean_true_add(json_interface_sub, + "ospfEnabled"); + } else { + if (ospf_oi_count(ifp) == 0) { + vty_out(vty, " OSPF not enabled on this interface\n"); + return; + } else if (!is_up) { + vty_out(vty, + " OSPF is enabled, but not running on this interface\n"); + return; + } + } + + if (use_json) { + json_ois = json_object_new_object(); + json_object_object_add(json_interface_sub, "interfaceIp", + json_ois); + } + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi = rn->info; + + if (oi == NULL) + continue; + +#if CONFDATE > 20240601 + CPP_NOTICE( + "Use all fields following ospfEnabled from interfaceIp hierarchy") +#endif + + if (use_json) + json_oi = json_object_new_object(); + + if (CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) { + if (use_json) { + json_object_boolean_true_add(json_interface_sub, + "ifUnnumbered"); + json_object_boolean_true_add(json_oi, + "ifUnnumbered"); + } else + vty_out(vty, " This interface is UNNUMBERED,"); + } else { + struct in_addr dest; + const char *dstr; + + /* Show OSPF interface information. */ + if (use_json) { + json_object_string_addf( + json_interface_sub, "ipAddress", "%pI4", + &oi->address->u.prefix4); + json_object_int_add(json_interface_sub, + "ipAddressPrefixlen", + oi->address->prefixlen); + + json_object_string_addf( + json_oi, "ipAddress", "%pI4", + &oi->address->u.prefix4); + json_object_int_add(json_oi, + "ipAddressPrefixlen", + oi->address->prefixlen); + } else + vty_out(vty, " Internet Address %pFX,", + oi->address); + + /* For Vlinks, showing the peer address is + * probably more informative than the local + * interface that is being used */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) { + dstr = "Peer"; + dest = oi->vl_data->peer_addr; + } else if (CONNECTED_PEER(oi->connected) + && oi->connected->destination) { + dstr = "Peer"; + dest = oi->connected->destination->u.prefix4; + } else { + dstr = "Broadcast"; + dest.s_addr = ipv4_broadcast_addr( + oi->connected->address->u.prefix4.s_addr, + oi->connected->address->prefixlen); + } + + if (use_json) { + json_object_string_add(json_interface_sub, + "ospfIfType", dstr); + + json_object_string_add(json_oi, "ospfIfType", + dstr); + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) { + json_object_string_addf( + json_interface_sub, "vlinkPeer", + "%pI4", &dest); + + json_object_string_addf(json_oi, + "vlinkPeer", + "%pI4", &dest); + } else { + json_object_string_addf( + json_interface_sub, + "localIfUsed", "%pI4", &dest); + + json_object_string_addf(json_oi, + "localIfUsed", + "%pI4", &dest); + } + } else + vty_out(vty, " %s %pI4,", dstr, + &dest); + } + if (use_json) { + json_object_string_add(json_interface_sub, "area", + ospf_area_desc_string(oi->area)); + + json_object_string_add(json_oi, "area", + ospf_area_desc_string(oi->area)); + + if (OSPF_IF_PARAM(oi, mtu_ignore)) { + json_object_boolean_true_add( + json_oi, "mtuMismatchDetect"); + json_object_boolean_true_add( + json_interface_sub, + "mtuMismatchDetect"); + } + + json_object_string_addf(json_interface_sub, "routerId", + "%pI4", &ospf->router_id); + json_object_string_add(json_interface_sub, + "networkType", + ospf_network_type_str[oi->type]); + json_object_int_add(json_interface_sub, "cost", + oi->output_cost); + json_object_int_add(json_interface_sub, + "transmitDelaySecs", + OSPF_IF_PARAM(oi, transmit_delay)); + json_object_string_add(json_interface_sub, "state", + lookup_msg(ospf_ism_state_msg, + oi->state, NULL)); + json_object_int_add(json_interface_sub, "priority", + PRIORITY(oi)); + + json_object_string_addf(json_oi, "routerId", "%pI4", + &ospf->router_id); + json_object_string_add(json_oi, "networkType", + ospf_network_type_str[oi->type]); + json_object_int_add(json_oi, "cost", oi->output_cost); + json_object_int_add(json_oi, "transmitDelaySecs", + OSPF_IF_PARAM(oi, transmit_delay)); + json_object_string_add(json_oi, "state", + lookup_msg(ospf_ism_state_msg, + oi->state, NULL)); + json_object_int_add(json_oi, "priority", PRIORITY(oi)); + json_object_boolean_add( + json_interface_sub, "opaqueCapable", + OSPF_IF_PARAM(oi, opaque_capable)); + } else { + vty_out(vty, " Area %s\n", + ospf_area_desc_string(oi->area)); + + vty_out(vty, " MTU mismatch detection: %s\n", + OSPF_IF_PARAM(oi, mtu_ignore) ? "disabled" + : "enabled"); + + vty_out(vty, + " Router ID %pI4, Network Type %s, Cost: %d\n", + &ospf->router_id, + ospf_network_type_str[oi->type], + oi->output_cost); + + vty_out(vty, + " Transmit Delay is %d sec, State %s, Priority %d\n", + OSPF_IF_PARAM(oi, transmit_delay), + lookup_msg(ospf_ism_state_msg, oi->state, NULL), + PRIORITY(oi)); + if (!OSPF_IF_PARAM(oi, opaque_capable)) + vty_out(vty, + " Opaque LSA capability disabled on interface\n"); + } + + /* Show DR information. */ + if (DR(oi).s_addr == INADDR_ANY) { + if (!use_json) + vty_out(vty, + " No backup designated router on this network\n"); + } else { + nbr = ospf_nbr_lookup_by_addr(oi->nbrs, &DR(oi)); + if (nbr) { + if (use_json) { + json_object_string_addf( + json_interface_sub, "drId", + "%pI4", &nbr->router_id); + json_object_string_addf( + json_interface_sub, "drAddress", + "%pI4", + &nbr->address.u.prefix4); + + json_object_string_addf( + json_oi, "drId", "%pI4", + &nbr->router_id); + json_object_string_addf( + json_oi, "drAddress", "%pI4", + &nbr->address.u.prefix4); + } else { + vty_out(vty, + " Designated Router (ID) %pI4", + &nbr->router_id); + vty_out(vty, + " Interface Address %pFX\n", + &nbr->address); + } + } + nbr = NULL; + + nbr = ospf_nbr_lookup_by_addr(oi->nbrs, &BDR(oi)); + if (nbr == NULL) { + if (!use_json) + vty_out(vty, + " No backup designated router on this network\n"); + } else { + if (use_json) { + json_object_string_addf( + json_interface_sub, "bdrId", + "%pI4", &nbr->router_id); + json_object_string_addf( + json_interface_sub, + "bdrAddress", "%pI4", + &nbr->address.u.prefix4); + + json_object_string_addf( + json_oi, "bdrId", "%pI4", + &nbr->router_id); + json_object_string_addf( + json_oi, "bdrAddress", "%pI4", + &nbr->address.u.prefix4); + } else { + vty_out(vty, + " Backup Designated Router (ID) %pI4,", + &nbr->router_id); + vty_out(vty, " Interface Address %pI4\n", + &nbr->address.u.prefix4); + } + } + } + + /* Next network-LSA sequence number we'll use, if we're elected + * DR */ + if (oi->params + && ntohl(oi->params->network_lsa_seqnum) + != OSPF_INITIAL_SEQUENCE_NUMBER) { + if (use_json) { + json_object_int_add( + json_interface_sub, + "networkLsaSequence", + ntohl(oi->params->network_lsa_seqnum)); + + json_object_int_add( + json_oi, "networkLsaSequence", + ntohl(oi->params->network_lsa_seqnum)); + } else { + vty_out(vty, + " Saved Network-LSA sequence number 0x%x\n", + ntohl(oi->params->network_lsa_seqnum)); + } + } + + if (use_json) { + if (OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS) + || OI_MEMBER_CHECK(oi, MEMBER_DROUTERS)) { + if (OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS)) { + json_object_boolean_true_add( + json_interface_sub, + "mcastMemberOspfAllRouters"); + + json_object_boolean_true_add( + json_oi, + "mcastMemberOspfAllRouters"); + } + if (OI_MEMBER_CHECK(oi, MEMBER_DROUTERS)) { + json_object_boolean_true_add( + json_interface_sub, + "mcastMemberOspfDesignatedRouters"); + + json_object_boolean_true_add( + json_oi, + "mcastMemberOspfDesignatedRouters"); + } + } + } else { + vty_out(vty, " Multicast group memberships:"); + if (OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS) + || OI_MEMBER_CHECK(oi, MEMBER_DROUTERS)) { + if (OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS)) + vty_out(vty, " OSPFAllRouters"); + if (OI_MEMBER_CHECK(oi, MEMBER_DROUTERS)) + vty_out(vty, " OSPFDesignatedRouters"); + } else + vty_out(vty, " "); + vty_out(vty, "\n"); + } + + if (use_json) { + if (OSPF_IF_PARAM(oi, fast_hello) == 0) { + json_object_int_add( + json_interface_sub, "timerMsecs", + OSPF_IF_PARAM(oi, v_hello) * 1000); + + json_object_int_add(json_oi, "timerMsecs", + OSPF_IF_PARAM(oi, v_hello) * + 1000); + } else { + json_object_int_add( + json_interface_sub, "timerMsecs", + 1000 / OSPF_IF_PARAM(oi, fast_hello)); + + json_object_int_add( + json_oi, "timerMsecs", + 1000 / OSPF_IF_PARAM(oi, fast_hello)); + } + json_object_int_add(json_interface_sub, "timerDeadSecs", + OSPF_IF_PARAM(oi, v_wait)); + json_object_int_add(json_interface_sub, "timerWaitSecs", + OSPF_IF_PARAM(oi, v_wait)); + json_object_int_add( + json_interface_sub, "timerRetransmitSecs", + OSPF_IF_PARAM(oi, retransmit_interval)); + + json_object_int_add(json_oi, "timerDeadSecs", + OSPF_IF_PARAM(oi, v_wait)); + json_object_int_add(json_oi, "timerWaitSecs", + OSPF_IF_PARAM(oi, v_wait)); + json_object_int_add( + json_oi, "timerRetransmitSecs", + OSPF_IF_PARAM(oi, retransmit_interval)); + } else { + vty_out(vty, " Timer intervals configured,"); + vty_out(vty, " Hello "); + if (OSPF_IF_PARAM(oi, fast_hello) == 0) + vty_out(vty, "%ds,", + OSPF_IF_PARAM(oi, v_hello)); + else + vty_out(vty, "%dms,", + 1000 / OSPF_IF_PARAM(oi, fast_hello)); + vty_out(vty, " Dead %ds, Wait %ds, Retransmit %d\n", + OSPF_IF_PARAM(oi, v_wait), + OSPF_IF_PARAM(oi, v_wait), + OSPF_IF_PARAM(oi, retransmit_interval)); + } + + if (OSPF_IF_PASSIVE_STATUS(oi) == OSPF_IF_ACTIVE) { + char timebuf[OSPF_TIME_DUMP_SIZE]; + if (use_json) { + long time_store = 0; + if (oi->t_hello) + time_store = + monotime_until( + &oi->t_hello->u.sands, + NULL) + / 1000LL; + json_object_int_add(json_interface_sub, + "timerHelloInMsecs", + time_store); + json_object_int_add(json_oi, + "timerHelloInMsecs", + time_store); + } else + vty_out(vty, " Hello due in %s\n", + ospf_timer_dump(oi->t_hello, timebuf, + sizeof(timebuf))); + } else /* passive-interface is set */ + { + if (use_json) { + json_object_boolean_true_add( + json_interface_sub, + "timerPassiveIface"); + + json_object_boolean_true_add( + json_oi, "timerPassiveIface"); + } else + vty_out(vty, + " No Hellos (Passive interface)\n"); + } + + if (use_json) { + json_object_int_add(json_interface_sub, "nbrCount", + ospf_nbr_count(oi, 0)); + json_object_int_add(json_interface_sub, + "nbrAdjacentCount", + ospf_nbr_count(oi, NSM_Full)); + + json_object_int_add(json_oi, "nbrCount", + ospf_nbr_count(oi, 0)); + json_object_int_add(json_oi, "nbrAdjacentCount", + ospf_nbr_count(oi, NSM_Full)); + } else + vty_out(vty, + " Neighbor Count is %d, Adjacent neighbor count is %d\n", + ospf_nbr_count(oi, 0), + ospf_nbr_count(oi, NSM_Full)); + + params = IF_DEF_PARAMS(ifp); + if (params && + OSPF_IF_PARAM_CONFIGURED(params, v_gr_hello_delay)) { + if (use_json) { + json_object_int_add(json_interface_sub, + "grHelloDelaySecs", + params->v_gr_hello_delay); + + json_object_int_add(json_oi, "grHelloDelaySecs", + params->v_gr_hello_delay); + } else + vty_out(vty, + " Graceful Restart hello delay: %us\n", + params->v_gr_hello_delay); + } + + ospf_interface_bfd_show(vty, ifp, json_interface_sub); + + if (use_json) { + json_object_boolean_add(json_interface_sub, + "prefixSuppression", + OSPF_IF_PARAM(oi, + prefix_suppression)); + json_object_boolean_add(json_oi, "prefixSuppression", + OSPF_IF_PARAM(oi, + prefix_suppression)); + } else { + if (OSPF_IF_PARAM(oi, prefix_suppression)) + vty_out(vty, + " Suppress advertisement of interface IP prefix\n"); + } + + /* OSPF Authentication information */ + ospf_interface_auth_show(vty, oi, json_interface_sub, use_json); + + ospf_interface_auth_show(vty, oi, json_oi, use_json); + if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) { + if (use_json) { + json_object_boolean_add(json_interface_sub, + "p2mpDelayReflood", + oi->p2mp_delay_reflood); + + json_object_boolean_add(json_oi, + "p2mpDelayReflood", + oi->p2mp_delay_reflood); + } else { + vty_out(vty, + " %sDelay reflooding LSAs received on P2MP interface\n", + oi->p2mp_delay_reflood ? "" : "Don't "); + } + } + + /* Add ospf_interface object to main json blob using SIP as key + */ + if (use_json) + json_object_object_addf(json_ois, json_oi, "%pI4", + &oi->address->u.prefix4); + } +} + +static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf, + char *intf_name, uint8_t use_vrf, + json_object *json, bool use_json) +{ + struct interface *ifp; + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + json_object *json_vrf = NULL; + json_object *json_interface_sub = NULL, *json_interface = NULL; + + if (use_json) { + if (use_vrf) + json_vrf = json_object_new_object(); + else + json_vrf = json; + json_interface = json_object_new_object(); + } + + if (ospf->instance) { + if (use_json) + json_object_int_add(json, "ospfInstance", + ospf->instance); + else + vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); + } + + ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf); + + if (intf_name == NULL) { + /* Show All Interfaces.*/ + FOR_ALL_INTERFACES (vrf, ifp) { + if (ospf_oi_count(ifp)) { + if (use_json) { + json_interface_sub = + json_object_new_object(); + } + show_ip_ospf_interface_sub(vty, ospf, ifp, + json_interface_sub, + use_json); + + if (use_json) { + json_object_object_add( + json_interface, ifp->name, + json_interface_sub); + } + } + } + if (use_json) + json_object_object_add(json_vrf, "interfaces", + json_interface); + } else { + /* Interface name is specified. */ + ifp = if_lookup_by_name(intf_name, ospf->vrf_id); + if (ifp == NULL) { + if (use_json) { + json_object_boolean_true_add(json_vrf, + "noSuchIface"); + json_object_free(json_interface); + } else + vty_out(vty, "No such interface name\n"); + } else { + if (use_json) + json_interface_sub = json_object_new_object(); + + show_ip_ospf_interface_sub( + vty, ospf, ifp, json_interface_sub, use_json); + + if (use_json) { + json_object_object_add(json_interface, + ifp->name, + json_interface_sub); + json_object_object_add(json_vrf, "interfaces", + json_interface); + } + } + } + + if (use_json) { + if (use_vrf) { + json_object_object_add(json, ospf_get_name(ospf), + json_vrf); + } + } else + vty_out(vty, "\n"); + + return CMD_SUCCESS; +} + +static void show_ip_ospf_interface_traffic_sub(struct vty *vty, + struct ospf_interface *oi, + json_object *json_interface_sub, + bool use_json) +{ + if (use_json) { + json_object_int_add(json_interface_sub, "ifIndex", + oi->ifp->ifindex); + json_object_int_add(json_interface_sub, "helloIn", + oi->hello_in); + json_object_int_add(json_interface_sub, "helloOut", + oi->hello_out); + json_object_int_add(json_interface_sub, "dbDescIn", + oi->db_desc_in); + json_object_int_add(json_interface_sub, "dbDescOut", + oi->db_desc_out); + json_object_int_add(json_interface_sub, "lsReqIn", + oi->ls_req_in); + json_object_int_add(json_interface_sub, "lsReqOut", + oi->ls_req_out); + json_object_int_add(json_interface_sub, "lsUpdIn", + oi->ls_upd_in); + json_object_int_add(json_interface_sub, "lsUpdOut", + oi->ls_upd_out); + json_object_int_add(json_interface_sub, "lsAckIn", + oi->ls_ack_in); + json_object_int_add(json_interface_sub, "lsAckOut", + oi->ls_ack_out); + json_object_int_add(json_interface_sub, "packetsQueued", + listcount(oi->obuf)); + } else { + vty_out(vty, + "%-10s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %12lu\n", + oi->ifp->name, oi->hello_in, oi->hello_out, + oi->db_desc_in, oi->db_desc_out, oi->ls_req_in, + oi->ls_req_out, oi->ls_upd_in, oi->ls_upd_out, + oi->ls_ack_in, oi->ls_ack_out, listcount(oi->obuf)); + } +} + +/* OSPFv2 Packet Counters */ +static int show_ip_ospf_interface_traffic_common( + struct vty *vty, struct ospf *ospf, char *intf_name, json_object *json, + int display_once, uint8_t use_vrf, bool use_json) +{ + struct vrf *vrf = NULL; + struct interface *ifp = NULL; + json_object *json_vrf = NULL; + json_object *json_interface_sub = NULL; + + if (!use_json && !display_once) { + vty_out(vty, "\n"); + vty_out(vty, "%-12s%-17s%-17s%-17s%-17s%-17s%-17s\n", + "Interface", " HELLO", " DB-Desc", " LS-Req", + " LS-Update", " LS-Ack", " Packets"); + vty_out(vty, "%-10s%-18s%-18s%-17s%-17s%-17s%-17s\n", "", + " Rx/Tx", " Rx/Tx", " Rx/Tx", " Rx/Tx", + " Rx/Tx", " Queued"); + vty_out(vty, + "-------------------------------------------------------------------------------------------------------------\n"); + } else if (use_json) { + if (use_vrf) + json_vrf = json_object_new_object(); + else + json_vrf = json; + } + + ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf); + + if (intf_name == NULL) { + vrf = vrf_lookup_by_id(ospf->vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) { + struct route_node *rn; + struct ospf_interface *oi; + + if (ospf_oi_count(ifp) == 0) + continue; + + for (rn = route_top(IF_OIFS(ifp)); rn; + rn = route_next(rn)) { + oi = rn->info; + + if (oi == NULL) + continue; + + if (use_json) { + json_interface_sub = + json_object_new_object(); + } + + show_ip_ospf_interface_traffic_sub( + vty, oi, json_interface_sub, use_json); + if (use_json) { + json_object_object_add( + json_vrf, ifp->name, + json_interface_sub); + } + } + } + } else { + /* Interface name is specified. */ + ifp = if_lookup_by_name(intf_name, ospf->vrf_id); + if (ifp != NULL) { + struct route_node *rn; + struct ospf_interface *oi; + + if (ospf_oi_count(ifp) == 0) { + vty_out(vty, + " OSPF not enabled on this interface %s\n", + ifp->name); + return CMD_SUCCESS; + } + + for (rn = route_top(IF_OIFS(ifp)); rn; + rn = route_next(rn)) { + oi = rn->info; + + if (oi == NULL) + continue; + + if (use_json) { + json_interface_sub = + json_object_new_object(); + } + + show_ip_ospf_interface_traffic_sub( + vty, oi, json_interface_sub, use_json); + if (use_json) { + json_object_object_add( + json_vrf, ifp->name, + json_interface_sub); + } + } + } + } + + if (use_json) { + if (use_vrf) + json_object_object_add(json, ospf_get_name(ospf), + json_vrf); + } else + vty_out(vty, "\n"); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_interface, + show_ip_ospf_interface_cmd, + "show ip ospf [vrf ] interface [INTERFACE] [json]", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "All VRFs\n" + "Interface information\n" + "Interface name\n" + JSON_STR) +{ + struct ospf *ospf; + bool uj = use_json(argc, argv); + struct listnode *node = NULL; + char *vrf_name = NULL, *intf_name = NULL; + bool all_vrf = false; + int ret = CMD_SUCCESS; + int inst = 0; + int idx_vrf = 0, idx_intf = 0; + uint8_t use_vrf = 0; + json_object *json = NULL; + + OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + if (argv_find(argv, argc, "INTERFACE", &idx_intf)) + intf_name = argv[idx_intf]->arg; + + if (uj) + json = json_object_new_object(); + + /* vrf input is provided could be all or specific vrf*/ + if (vrf_name) { + use_vrf = 1; + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + ret = show_ip_ospf_interface_common( + vty, ospf, intf_name, use_vrf, json, + uj); + } + + if (uj) + vty_json(vty, json); + else if (!ospf) + vty_out(vty, "%% OSPF is not enabled\n"); + + return ret; + } + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + vty_json(vty, json); + else + vty_out(vty, + "%% OSPF is not enabled in vrf %s\n", + vrf_name); + + return CMD_SUCCESS; + } + ret = show_ip_ospf_interface_common(vty, ospf, intf_name, + use_vrf, json, uj); + + } else { + /* Display default ospf (instance 0) info */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + vty_json(vty, json); + else + vty_out(vty, + "%% OSPF is not enabled in vrf default\n"); + + return CMD_SUCCESS; + } + ret = show_ip_ospf_interface_common(vty, ospf, intf_name, + use_vrf, json, uj); + } + + if (uj) + vty_json(vty, json); + + return ret; +} + +DEFUN (show_ip_ospf_instance_interface, + show_ip_ospf_instance_interface_cmd, + "show ip ospf (1-65535) interface [INTERFACE] [json]", + SHOW_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + "Interface information\n" + "Interface name\n" + JSON_STR) +{ + int idx_number = 3; + int idx_intf = 0; + struct ospf *ospf; + unsigned short instance = 0; + bool uj = use_json(argc, argv); + char *intf_name = NULL; + int ret = CMD_SUCCESS; + json_object *json = NULL; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + ospf = ospf_lookup_instance(instance); + if (!ospf || !ospf->oi_running) + return CMD_SUCCESS; + + if (uj) + json = json_object_new_object(); + + if (argv_find(argv, argc, "INTERFACE", &idx_intf)) + intf_name = argv[idx_intf]->arg; + + ret = show_ip_ospf_interface_common(vty, ospf, intf_name, 0, json, uj); + + if (uj) + vty_json(vty, json); + + return ret; +} + +DEFUN (show_ip_ospf_interface_traffic, + show_ip_ospf_interface_traffic_cmd, + "show ip ospf [vrf ] interface traffic [INTERFACE] [json]", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "All VRFs\n" + "Interface information\n" + "Protocol Packet counters\n" + "Interface name\n" + JSON_STR) +{ + struct ospf *ospf = NULL; + struct listnode *node = NULL; + char *vrf_name = NULL, *intf_name = NULL; + bool all_vrf = false; + int inst = 0; + int idx_vrf = 0, idx_intf = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; + int ret = CMD_SUCCESS; + int display_once = 0; + uint8_t use_vrf = 0; + + OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + if (argv_find(argv, argc, "INTERFACE", &idx_intf)) + intf_name = argv[idx_intf]->arg; + + if (uj) + json = json_object_new_object(); + + if (vrf_name) { + use_vrf = 1; + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + + ret = show_ip_ospf_interface_traffic_common( + vty, ospf, intf_name, json, + display_once, use_vrf, uj); + display_once = 1; + } + + if (uj) + vty_json(vty, json); + + return ret; + } + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + json_object_free(json); + return CMD_SUCCESS; + } + + ret = show_ip_ospf_interface_traffic_common( + vty, ospf, intf_name, json, display_once, use_vrf, uj); + } else { + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + json_object_free(json); + return CMD_SUCCESS; + } + + ret = show_ip_ospf_interface_traffic_common( + vty, ospf, intf_name, json, display_once, use_vrf, uj); + } + + if (uj) + vty_json(vty, json); + + return ret; +} + + +static void show_ip_ospf_neighbour_header(struct vty *vty) +{ + vty_out(vty, "\n%-15s %-3s %-15s %-15s %-9s %-15s %-32s %5s %5s %5s\n", + "Neighbor ID", "Pri", "State", "Up Time", "Dead Time", + "Address", "Interface", "RXmtL", "RqstL", "DBsmL"); +} + +static void show_ip_ospf_neighbour_brief(struct vty *vty, + struct ospf_neighbor *nbr, + struct ospf_neighbor *prev_nbr, + json_object *json, bool use_json) +{ + char msgbuf[16]; + char timebuf[OSPF_TIME_DUMP_SIZE]; + json_object *json_neighbor = NULL, *json_neigh_array = NULL; + struct timeval res = {.tv_sec = 0, .tv_usec = 0}; + long time_val = 0; + char uptime[OSPF_TIME_DUMP_SIZE]; + + if (nbr->ts_last_progress.tv_sec || nbr->ts_last_progress.tv_usec) + time_val = + monotime_since(&nbr->ts_last_progress, &res) / 1000LL; + + if (use_json) { + char neigh_str[INET_ADDRSTRLEN]; + + if (prev_nbr && !IPV4_ADDR_SAME(&prev_nbr->src, &nbr->src)) { + /* Start new neigh list */ + json_neigh_array = NULL; + } + + if (nbr->state == NSM_Attempt && + nbr->router_id.s_addr == INADDR_ANY) + strlcpy(neigh_str, "neighbor", sizeof(neigh_str)); + else + inet_ntop(AF_INET, &nbr->router_id, neigh_str, + sizeof(neigh_str)); + + json_object_object_get_ex(json, neigh_str, &json_neigh_array); + + if (!json_neigh_array) { + json_neigh_array = json_object_new_array(); + json_object_object_add(json, neigh_str, + json_neigh_array); + } + + json_neighbor = json_object_new_object(); + + ospf_nbr_ism_state_message(nbr, msgbuf, sizeof(msgbuf)); + json_object_string_add(json_neighbor, "nbrState", msgbuf); + + json_object_int_add(json_neighbor, "nbrPriority", + nbr->priority); + + json_object_string_add( + json_neighbor, "converged", + lookup_msg(ospf_nsm_state_msg, nbr->state, NULL)); + json_object_string_add(json_neighbor, "role", + lookup_msg(ospf_ism_state_msg, + ospf_nbr_ism_state(nbr), + NULL)); + if (nbr->t_inactivity) { + long time_store; + + time_store = monotime_until(&nbr->t_inactivity->u.sands, + NULL) / + 1000LL; + json_object_int_add(json_neighbor, "upTimeInMsec", + time_val); + json_object_int_add(json_neighbor, + "routerDeadIntervalTimerDueMsec", + time_store); + json_object_string_add( + json_neighbor, "upTime", + ospf_timeval_dump(&res, uptime, + sizeof(uptime))); + json_object_string_add( + json_neighbor, "deadTime", + ospf_timer_dump(nbr->t_inactivity, timebuf, + sizeof(timebuf))); + } else { + json_object_string_add(json_neighbor, "deadTimeMsecs", + "inactive"); + json_object_string_add(json_neighbor, + "routerDeadIntervalTimerDueMsec", + "inactive"); + } + json_object_string_addf(json_neighbor, "ifaceAddress", "%pI4", + &nbr->src); + json_object_string_add(json_neighbor, "ifaceName", + IF_NAME(nbr->oi)); + json_object_int_add(json_neighbor, + "linkStateRetransmissionListCounter", + ospf_ls_retransmit_count(nbr)); + json_object_int_add(json_neighbor, + "linkStateRequestListCounter", + ospf_ls_request_count(nbr)); + json_object_int_add(json_neighbor, "databaseSummaryListCounter", + ospf_db_summary_count(nbr)); + + json_object_array_add(json_neigh_array, json_neighbor); + } else { + ospf_nbr_ism_state_message(nbr, msgbuf, sizeof(msgbuf)); + + if (nbr->state == NSM_Attempt && + nbr->router_id.s_addr == INADDR_ANY) + vty_out(vty, "%-15s %3d %-15s ", "-", nbr->priority, + msgbuf); + else + vty_out(vty, "%-15pI4 %3d %-15s ", &nbr->router_id, + nbr->priority, msgbuf); + + vty_out(vty, "%-15s ", + ospf_timeval_dump(&res, uptime, sizeof(uptime))); + + vty_out(vty, "%9s ", + ospf_timer_dump(nbr->t_inactivity, timebuf, + sizeof(timebuf))); + vty_out(vty, "%-15pI4 ", &nbr->src); + vty_out(vty, "%-32s %5ld %5ld %5d\n", IF_NAME(nbr->oi), + ospf_ls_retransmit_count(nbr), + ospf_ls_request_count(nbr), ospf_db_summary_count(nbr)); + } +} + +static void show_ip_ospf_neighbor_sub(struct vty *vty, + struct ospf_interface *oi, + json_object *json, bool use_json) +{ + struct route_node *rn; + struct ospf_neighbor *nbr, *prev_nbr = NULL; + + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + nbr = rn->info; + + if (!nbr) + continue; + + /* Do not show myself. */ + if (nbr == oi->nbr_self) + continue; + /* Down state is not shown. */ + if (nbr->state == NSM_Down) + continue; + + prev_nbr = nbr; + + show_ip_ospf_neighbour_brief(vty, nbr, prev_nbr, json, + use_json); + } +} + +static int show_ip_ospf_neighbor_common(struct vty *vty, struct ospf *ospf, + json_object *json, bool use_json, + uint8_t use_vrf) +{ + struct ospf_interface *oi; + struct listnode *node; + json_object *json_vrf = NULL; + json_object *json_nbr_sub = NULL; + + if (use_json) { + if (use_vrf) + json_vrf = json_object_new_object(); + else + json_vrf = json; + json_nbr_sub = json_object_new_object(); + } + + if (ospf->instance) { + if (use_json) + json_object_int_add(json, "ospfInstance", + ospf->instance); + else + vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); + } + + ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf); + if (!use_json) + show_ip_ospf_neighbour_header(vty); + + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + if (ospf_interface_neighbor_count(oi) == 0) + continue; + show_ip_ospf_neighbor_sub(vty, oi, json_nbr_sub, use_json); + } + + if (use_json) { + json_object_object_add(json_vrf, "neighbors", json_nbr_sub); + if (use_vrf) + json_object_object_add(json, ospf_get_name(ospf), + json_vrf); + } else + vty_out(vty, "\n"); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_neighbor, + show_ip_ospf_neighbor_cmd, + "show ip ospf [vrf ] neighbor [json]", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "All VRFs\n" + "Neighbor list\n" + JSON_STR) +{ + struct ospf *ospf; + bool uj = use_json(argc, argv); + struct listnode *node = NULL; + char *vrf_name = NULL; + bool all_vrf = false; + int ret = CMD_SUCCESS; + int inst = 0; + int idx_vrf = 0; + uint8_t use_vrf = 0; + json_object *json = NULL; + + OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + if (uj) + json = json_object_new_object(); + + /* vrf input is provided could be all or specific vrf*/ + if (vrf_name) { + use_vrf = 1; + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + ret = show_ip_ospf_neighbor_common( + vty, ospf, json, uj, use_vrf); + } + + if (uj) + vty_json(vty, json); + else if (!ospf) + vty_out(vty, "OSPF is not enabled\n"); + + return ret; + } + + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + vty_json(vty, json); + else + vty_out(vty, + "%% OSPF is not enabled in vrf %s\n", + vrf_name); + + return CMD_SUCCESS; + } + } else { + /* Display default ospf (instance 0) info */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + vty_json(vty, json); + else + vty_out(vty, + "%% OSPF is not enabled in vrf default\n"); + + return CMD_SUCCESS; + } + } + + if (ospf) { + ret = show_ip_ospf_neighbor_common(vty, ospf, json, uj, + use_vrf); + + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + } + } + + if (uj) + json_object_free(json); + + return ret; +} + + +DEFUN (show_ip_ospf_instance_neighbor, + show_ip_ospf_instance_neighbor_cmd, + "show ip ospf (1-65535) neighbor [json]", + SHOW_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + "Neighbor list\n" + JSON_STR) +{ + int idx_number = 3; + struct ospf *ospf; + unsigned short instance = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; + int ret = CMD_SUCCESS; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + ospf = ospf_lookup_instance(instance); + if (!ospf || !ospf->oi_running) + return CMD_SUCCESS; + + if (uj) + json = json_object_new_object(); + + ret = show_ip_ospf_neighbor_common(vty, ospf, json, uj, 0); + + if (uj) + vty_json(vty, json); + + return ret; +} + +static int show_ip_ospf_neighbor_all_common(struct vty *vty, struct ospf *ospf, + json_object *json, bool use_json, + uint8_t use_vrf) +{ + struct listnode *node; + struct ospf_interface *oi; + char buf[PREFIX_STRLEN]; + json_object *json_vrf = NULL; + json_object *json_neighbor_sub = NULL; + + if (use_json) { + if (use_vrf) + json_vrf = json_object_new_object(); + else + json_vrf = json; + } + + ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf); + if (!use_json) + show_ip_ospf_neighbour_header(vty); + + if (ospf->instance) { + if (use_json) + json_object_int_add(json_vrf, "ospfInstance", + ospf->instance); + else + vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); + } + + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + struct listnode *nbr_node; + struct ospf_nbr_nbma *nbr_nbma; + + show_ip_ospf_neighbor_sub(vty, oi, json_vrf, use_json); + + /* print Down neighbor status */ + for (ALL_LIST_ELEMENTS_RO(oi->nbr_nbma, nbr_node, nbr_nbma)) { + if (nbr_nbma->nbr == NULL + || nbr_nbma->nbr->state == NSM_Down) { + if (use_json) { + json_neighbor_sub = + json_object_new_object(); + json_object_int_add(json_neighbor_sub, + "nbrNbmaPriority", + nbr_nbma->priority); + json_object_boolean_true_add( + json_neighbor_sub, + "nbrNbmaDown"); + json_object_string_add( + json_neighbor_sub, + "nbrNbmaIfaceName", + IF_NAME(oi)); + json_object_int_add( + json_neighbor_sub, + "nbrNbmaRetransmitCounter", 0); + json_object_int_add( + json_neighbor_sub, + "nbrNbmaRequestCounter", 0); + json_object_int_add( + json_neighbor_sub, + "nbrNbmaDbSummaryCounter", 0); + json_object_object_add( + json_vrf, + inet_ntop(AF_INET, + &nbr_nbma->addr, buf, + sizeof(buf)), + json_neighbor_sub); + } else { + vty_out(vty, "%-15s %3d %-15s %9s ", + "-", nbr_nbma->priority, "Down", + "-"); + vty_out(vty, + "%-32pI4 %-20s %5d %5d %5d\n", + &nbr_nbma->addr, + IF_NAME(oi), 0, 0, 0); + } + } + } + } + + if (use_json) { + if (use_vrf) + json_object_object_add(json, ospf_get_name(ospf), + json_vrf); + } else + vty_out(vty, "\n"); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_neighbor_all, + show_ip_ospf_neighbor_all_cmd, + "show ip ospf [vrf ] neighbor all [json]", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "All VRFs\n" + "Neighbor list\n" + "include down status neighbor\n" + JSON_STR) +{ + struct ospf *ospf; + bool uj = use_json(argc, argv); + struct listnode *node = NULL; + char *vrf_name = NULL; + bool all_vrf = false; + int ret = CMD_SUCCESS; + int inst = 0; + int idx_vrf = 0; + uint8_t use_vrf = 0; + json_object *json = NULL; + + OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + if (uj) + json = json_object_new_object(); + + /* vrf input is provided could be all or specific vrf*/ + if (vrf_name) { + use_vrf = 1; + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + ret = show_ip_ospf_neighbor_all_common( + vty, ospf, json, uj, use_vrf); + } + + if (uj) + vty_json(vty, json); + + return ret; + } + + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + json_object_free(json); + return CMD_SUCCESS; + } + } else { + /* Display default ospf (instance 0) info */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + json_object_free(json); + return CMD_SUCCESS; + } + } + + if (ospf) { + ret = show_ip_ospf_neighbor_all_common(vty, ospf, json, uj, + use_vrf); + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + } + } + + if (uj) + json_object_free(json); + + return ret; +} + +DEFUN (show_ip_ospf_instance_neighbor_all, + show_ip_ospf_instance_neighbor_all_cmd, + "show ip ospf (1-65535) neighbor all [json]", + SHOW_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + "Neighbor list\n" + "include down status neighbor\n" + JSON_STR) +{ + int idx_number = 3; + struct ospf *ospf; + unsigned short instance = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; + int ret = CMD_SUCCESS; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + ospf = ospf_lookup_instance(instance); + if (!ospf || !ospf->oi_running) + return CMD_SUCCESS; + if (uj) + json = json_object_new_object(); + + ret = show_ip_ospf_neighbor_all_common(vty, ospf, json, uj, 0); + + if (uj) + vty_json(vty, json); + + return ret; +} + +static int show_ip_ospf_neighbor_int_common(struct vty *vty, struct ospf *ospf, + const char *ifname, bool use_json, + uint8_t use_vrf) +{ + struct interface *ifp; + struct route_node *rn; + json_object *json = NULL; + + if (use_json) + json = json_object_new_object(); + + if (ospf->instance) { + if (use_json) + json_object_int_add(json, "ospfInstance", + ospf->instance); + else + vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); + } + + ospf_show_vrf_name(ospf, vty, json, use_vrf); + + ifp = if_lookup_by_name(ifname, ospf->vrf_id); + if (!ifp) { + if (use_json) + json_object_boolean_true_add(json, "noSuchIface"); + else + vty_out(vty, "No such interface.\n"); + return CMD_WARNING; + } + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi = rn->info; + + if (oi == NULL) + continue; + + show_ip_ospf_neighbor_sub(vty, oi, json, use_json); + } + + if (use_json) + vty_json(vty, json); + else + vty_out(vty, "\n"); + + return CMD_SUCCESS; +} + +DEFPY(show_ip_ospf_instance_neighbor_int, + show_ip_ospf_instance_neighbor_int_cmd, + "show ip ospf (1-65535)$instance neighbor IFNAME$ifname [json$json]", + SHOW_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + "Neighbor list\n" + "Interface name\n" + JSON_STR) +{ + struct ospf *ospf; + + if (!json) + show_ip_ospf_neighbour_header(vty); + + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + ospf = ospf_lookup_instance(instance); + if (!ospf || !ospf->oi_running) + return CMD_SUCCESS; + + if (!json) + show_ip_ospf_neighbour_header(vty); + + return show_ip_ospf_neighbor_int_common(vty, ospf, ifname, !!json, 0); +} + +static void show_ip_ospf_nbr_nbma_detail_sub(struct vty *vty, + struct ospf_interface *oi, + struct ospf_nbr_nbma *nbr_nbma, + bool use_json, json_object *json) +{ + char timebuf[OSPF_TIME_DUMP_SIZE]; + json_object *json_sub = NULL; + + if (use_json) + json_sub = json_object_new_object(); + else /* Show neighbor ID. */ + vty_out(vty, " Neighbor %s,", "-"); + + /* Show interface address. */ + if (use_json) + json_object_string_addf(json_sub, "ifaceAddress", "%pI4", + &nbr_nbma->addr); + else + vty_out(vty, " interface address %pI4\n", + &nbr_nbma->addr); + + /* Show Area ID. */ + if (use_json) { + json_object_string_add(json_sub, "areaId", + ospf_area_desc_string(oi->area)); + json_object_string_add(json_sub, "iface", IF_NAME(oi)); + } else + vty_out(vty, " In the area %s via interface %s\n", + ospf_area_desc_string(oi->area), IF_NAME(oi)); + + /* Show neighbor priority and state. */ + if (use_json) { + json_object_int_add(json_sub, "nbrPriority", + nbr_nbma->priority); + json_object_string_add(json_sub, "nbrState", "down"); + } else + vty_out(vty, " Neighbor priority is %d, State is %s,", + nbr_nbma->priority, "Down"); + + /* Show state changes. */ + if (use_json) + json_object_int_add(json_sub, "stateChangeCounter", + nbr_nbma->state_change); + else + vty_out(vty, " %d state changes\n", nbr_nbma->state_change); + + /* Show PollInterval */ + if (use_json) + json_object_int_add(json_sub, "pollInterval", nbr_nbma->v_poll); + else + vty_out(vty, " Poll interval %d\n", nbr_nbma->v_poll); + + /* Show poll-interval timer. */ + if (nbr_nbma->t_poll) { + if (use_json) { + long time_store; + time_store = monotime_until(&nbr_nbma->t_poll->u.sands, + NULL) / 1000LL; + json_object_int_add(json_sub, + "pollIntervalTimerDueMsec", + time_store); + } else + vty_out(vty, " Poll timer due in %s\n", + ospf_timer_dump(nbr_nbma->t_poll, timebuf, + sizeof(timebuf))); + } + + /* Show poll-interval timer thread. */ + if (use_json) { + if (nbr_nbma->t_poll != NULL) + json_object_string_add(json_sub, + "pollIntervalTimerThread", "on"); + } else + vty_out(vty, " Thread Poll Timer %s\n", + nbr_nbma->t_poll != NULL ? "on" : "off"); + + if (use_json) + json_object_object_add(json, "noNbrId", json_sub); +} + +static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, + struct ospf_interface *oi, + struct ospf_neighbor *nbr, + struct ospf_neighbor *prev_nbr, + json_object *json, bool use_json) +{ + char timebuf[OSPF_TIME_DUMP_SIZE]; + json_object *json_neigh = NULL, *json_neigh_array = NULL; + char neigh_str[INET_ADDRSTRLEN] = {0}; + char neigh_state[16] = {0}; + struct ospf_neighbor *nbr_dr, *nbr_bdr; + + if (use_json) { + if (prev_nbr && + !IPV4_ADDR_SAME(&prev_nbr->src, &nbr->src)) { + json_neigh_array = NULL; + } + + if (nbr->state == NSM_Attempt + && nbr->router_id.s_addr == INADDR_ANY) + strlcpy(neigh_str, "noNbrId", sizeof(neigh_str)); + else + inet_ntop(AF_INET, &nbr->router_id, + neigh_str, sizeof(neigh_str)); + + json_object_object_get_ex(json, neigh_str, &json_neigh_array); + + if (!json_neigh_array) { + json_neigh_array = json_object_new_array(); + json_object_object_add(json, neigh_str, + json_neigh_array); + } + + json_neigh = json_object_new_object(); + + } else { + /* Show neighbor ID. */ + if (nbr->state == NSM_Attempt + && nbr->router_id.s_addr == INADDR_ANY) + vty_out(vty, " Neighbor %s,", "-"); + else + vty_out(vty, " Neighbor %pI4,", + &nbr->router_id); + } + + /* Show interface address. */ + if (use_json) + json_object_string_addf(json_neigh, "ifaceAddress", "%pI4", + &nbr->address.u.prefix4); + else + vty_out(vty, " interface address %pI4\n", + &nbr->address.u.prefix4); + + /* Show Area ID. */ + if (use_json) { + json_object_string_add(json_neigh, "areaId", + ospf_area_desc_string(oi->area)); + json_object_string_add(json_neigh, "ifaceName", oi->ifp->name); + if (oi->address) + json_object_string_addf(json_neigh, "localIfaceAddress", + "%pI4", + &oi->address->u.prefix4); + } else { + vty_out(vty, " In the area %s via interface %s", + ospf_area_desc_string(oi->area), oi->ifp->name); + if (oi->address) + vty_out(vty, " local interface IP %pI4\n", + &oi->address->u.prefix4); + else + vty_out(vty, "\n"); + } + + /* Show neighbor priority and state. */ + ospf_nbr_ism_state_message(nbr, neigh_state, sizeof(neigh_state)); + if (use_json) { + json_object_int_add(json_neigh, "nbrPriority", nbr->priority); + json_object_string_add(json_neigh, "nbrState", neigh_state); + json_object_string_add(json_neigh, "role", + lookup_msg(ospf_ism_state_msg, + ospf_nbr_ism_state(nbr), + NULL)); + } else { + vty_out(vty, + " Neighbor priority is %d, State is %s, Role is %s,", + nbr->priority, neigh_state, + lookup_msg(ospf_ism_state_msg, ospf_nbr_ism_state(nbr), + NULL)); + } + /* Show state changes. */ + if (use_json) + json_object_int_add(json_neigh, "stateChangeCounter", + nbr->state_change); + else + vty_out(vty, " %d state changes\n", nbr->state_change); + + if (nbr->ts_last_progress.tv_sec || nbr->ts_last_progress.tv_usec) { + struct timeval res; + long time_store; + + time_store = + monotime_since(&nbr->ts_last_progress, &res) / 1000LL; + if (use_json) { + json_object_int_add(json_neigh, "lastPrgrsvChangeMsec", + time_store); + } else { + vty_out(vty, + " Most recent state change statistics:\n"); + vty_out(vty, " Progressive change %s ago\n", + ospf_timeval_dump(&res, timebuf, + sizeof(timebuf))); + } + } + + if (nbr->ts_last_regress.tv_sec || nbr->ts_last_regress.tv_usec) { + struct timeval res; + long time_store; + + time_store = + monotime_since(&nbr->ts_last_regress, &res) / 1000LL; + if (use_json) { + json_object_int_add(json_neigh, + "lastRegressiveChangeMsec", + time_store); + if (nbr->last_regress_str) + json_object_string_add( + json_neigh, + "lastRegressiveChangeReason", + nbr->last_regress_str); + } else { + vty_out(vty, + " Regressive change %s ago, due to %s\n", + ospf_timeval_dump(&res, timebuf, + sizeof(timebuf)), + (nbr->last_regress_str ? nbr->last_regress_str + : "??")); + } + } + + /* Show Designated Router ID. */ + if (DR(oi).s_addr == INADDR_ANY) { + if (!use_json) + vty_out(vty, + " No designated router on this network\n"); + } else { + nbr_dr = ospf_nbr_lookup_by_addr(oi->nbrs, &DR(oi)); + if (nbr_dr) { + if (use_json) + json_object_string_addf( + json_neigh, "routerDesignatedId", + "%pI4", &nbr_dr->router_id); + else + vty_out(vty, " DR is %pI4,", + &nbr_dr->router_id); + } + } + + /* Show Backup Designated Router ID. */ + nbr_bdr = ospf_nbr_lookup_by_addr(oi->nbrs, &BDR(oi)); + if (nbr_bdr == NULL) { + if (!use_json) + vty_out(vty, + " No backup designated router on this network\n"); + } else { + if (use_json) + json_object_string_addf(json_neigh, + "routerDesignatedBackupId", + "%pI4", &nbr_bdr->router_id); + else + vty_out(vty, " BDR is %pI4\n", &nbr_bdr->router_id); + } + + /* Show options. */ + if (use_json) { + json_object_int_add(json_neigh, "optionsCounter", nbr->options); + json_object_string_add(json_neigh, "optionsList", + ospf_options_dump(nbr->options)); + } else + vty_out(vty, " Options %d %s\n", nbr->options, + ospf_options_dump(nbr->options)); + + /* Show Router Dead interval timer. */ + if (use_json) { + if (nbr->t_inactivity) { + long time_store; + time_store = monotime_until(&nbr->t_inactivity->u.sands, + NULL) + / 1000LL; + json_object_int_add(json_neigh, + "routerDeadIntervalTimerDueMsec", + time_store); + } else + json_object_int_add( + json_neigh, + "routerDeadIntervalTimerDueMsec", -1); + } else + vty_out(vty, " Dead timer due in %s\n", + ospf_timer_dump(nbr->t_inactivity, timebuf, + sizeof(timebuf))); + + /* Show Database Summary list. */ + if (use_json) + json_object_int_add(json_neigh, "databaseSummaryListCounter", + ospf_db_summary_count(nbr)); + else + vty_out(vty, " Database Summary List %d\n", + ospf_db_summary_count(nbr)); + + /* Show Link State Request list. */ + if (use_json) + json_object_int_add(json_neigh, "linkStateRequestListCounter", + ospf_ls_request_count(nbr)); + else + vty_out(vty, " Link State Request List %ld\n", + ospf_ls_request_count(nbr)); + + /* Show Link State Retransmission list. */ + if (use_json) + json_object_int_add(json_neigh, + "linkStateRetransmissionListCounter", + ospf_ls_retransmit_count(nbr)); + else + vty_out(vty, " Link State Retransmission List %ld\n", + ospf_ls_retransmit_count(nbr)); + + /* Show inactivity timer thread. */ + if (use_json) { + if (nbr->t_inactivity != NULL) + json_object_string_add(json_neigh, + "threadInactivityTimer", "on"); + } else + vty_out(vty, " Thread Inactivity Timer %s\n", + nbr->t_inactivity != NULL ? "on" : "off"); + + /* Show Database Description retransmission thread. */ + if (use_json) { + if (nbr->t_db_desc != NULL) + json_object_string_add( + json_neigh, + "threadDatabaseDescriptionRetransmission", + "on"); + } else + vty_out(vty, + " Thread Database Description Retransmision %s\n", + nbr->t_db_desc != NULL ? "on" : "off"); + + /* Show Link State Request Retransmission thread. */ + if (use_json) { + if (nbr->t_ls_req != NULL) + json_object_string_add( + json_neigh, + "threadLinkStateRequestRetransmission", "on"); + } else + vty_out(vty, + " Thread Link State Request Retransmission %s\n", + nbr->t_ls_req != NULL ? "on" : "off"); + + /* Show Link State Update Retransmission thread. */ + if (use_json) { + if (nbr->t_ls_upd != NULL) + json_object_string_add( + json_neigh, + "threadLinkStateUpdateRetransmission", + "on"); + } else + vty_out(vty, + " Thread Link State Update Retransmission %s\n\n", + nbr->t_ls_upd != NULL ? "on" : "off"); + + if (!use_json) { + vty_out(vty, " Graceful restart Helper info:\n"); + + if (OSPF_GR_IS_ACTIVE_HELPER(nbr)) { + vty_out(vty, + " Graceful Restart HELPER Status : Inprogress.\n"); + + vty_out(vty, + " Graceful Restart grace period time: %d (seconds).\n", + nbr->gr_helper_info.recvd_grace_period); + vty_out(vty, " Graceful Restart reason: %s.\n", + ospf_restart_reason2str( + nbr->gr_helper_info.gr_restart_reason)); + } else { + vty_out(vty, + " Graceful Restart HELPER Status : None\n"); + } + + if (nbr->gr_helper_info.rejected_reason + != OSPF_HELPER_REJECTED_NONE) + vty_out(vty, " Helper rejected reason: %s.\n", + ospf_rejected_reason2str( + nbr->gr_helper_info.rejected_reason)); + + if (nbr->gr_helper_info.helper_exit_reason + != OSPF_GR_HELPER_EXIT_NONE) + vty_out(vty, " Last helper exit reason: %s.\n\n", + ospf_exit_reason2str( + nbr->gr_helper_info.helper_exit_reason)); + else + vty_out(vty, "\n"); + } else { + json_object_string_add(json_neigh, "grHelperStatus", + OSPF_GR_IS_ACTIVE_HELPER(nbr) ? + "Inprogress" + : "None"); + if (OSPF_GR_IS_ACTIVE_HELPER(nbr)) { + json_object_int_add( + json_neigh, "graceInterval", + nbr->gr_helper_info.recvd_grace_period); + json_object_string_add( + json_neigh, "grRestartReason", + ospf_restart_reason2str( + nbr->gr_helper_info.gr_restart_reason)); + } + + if (nbr->gr_helper_info.rejected_reason + != OSPF_HELPER_REJECTED_NONE) + json_object_string_add( + json_neigh, "helperRejectReason", + ospf_rejected_reason2str( + nbr->gr_helper_info.rejected_reason)); + + if (nbr->gr_helper_info.helper_exit_reason + != OSPF_GR_HELPER_EXIT_NONE) + json_object_string_add( + json_neigh, "helperExitReason", + ospf_exit_reason2str( + nbr->gr_helper_info + .helper_exit_reason)); + } + + bfd_sess_show(vty, json_neigh, nbr->bfd_session); + + if (use_json) + json_object_array_add(json_neigh_array, json_neigh); + +} + +static int show_ip_ospf_neighbor_id_common(struct vty *vty, struct ospf *ospf, + struct in_addr *router_id, + bool use_json, uint8_t use_vrf, + bool is_detail, + json_object *json_vrf) +{ + struct listnode *node; + struct ospf_neighbor *nbr; + struct ospf_interface *oi; + json_object *json = NULL; + + if (use_json) + json = json_object_new_object(); + + if (ospf->instance) { + if (use_json) + json_object_int_add(json, "ospfInstance", + ospf->instance); + else + vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); + } + + ospf_show_vrf_name(ospf, vty, json, use_vrf); + + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + nbr = ospf_nbr_lookup_by_routerid(oi->nbrs, router_id); + + if (!nbr) + continue; + + if (is_detail) + show_ip_ospf_neighbor_detail_sub(vty, oi, nbr, NULL, + json, use_json); + else + show_ip_ospf_neighbour_brief(vty, nbr, NULL, json, + use_json); + } + + if (json_vrf && use_json) { + json_object_object_add( + json_vrf, + (ospf->vrf_id == VRF_DEFAULT) ? "default" : ospf->name, + json); + return CMD_SUCCESS; + } + + if (use_json) + vty_json(vty, json); + else + vty_out(vty, "\n"); + + return CMD_SUCCESS; +} + +DEFPY(show_ip_ospf_neighbor_id, + show_ip_ospf_neighbor_id_cmd, + "show ip ospf [vrf NAME$vrf_name] neighbor A.B.C.D$router_id [detail$detail] [json$json]", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "Neighbor list\n" + "Neighbor ID\n" + "Detailed output\n" + JSON_STR) +{ + struct ospf *ospf; + struct listnode *node; + int ret = CMD_SUCCESS; + int inst = 0; + + if (vrf_name && !strmatch(vrf_name, "all")) { + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + if (ospf == NULL || !ospf->oi_running) { + if (!json) + vty_out(vty, + "%% OSPF is not enabled in vrf %s\n", + vrf_name); + else + vty_json_empty(vty); + return CMD_SUCCESS; + } + ret = show_ip_ospf_neighbor_id_common( + vty, ospf, &router_id, !!json, 0, !!detail, NULL); + } else { + json_object *json_vrf = NULL; + + if (json) + json_vrf = json_object_new_object(); + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + ret = show_ip_ospf_neighbor_id_common( + vty, ospf, &router_id, !!json, 0, !!detail, + json_vrf); + } + if (json) + vty_json(vty, json_vrf); + } + + return ret; +} + +DEFPY(show_ip_ospf_instance_neighbor_id, show_ip_ospf_instance_neighbor_id_cmd, + "show ip ospf (1-65535)$instance neighbor A.B.C.D$router_id [detail$detail] [json$json]", + SHOW_STR IP_STR + "OSPF information\n" + "Instance ID\n" + "Neighbor list\n" + "Neighbor ID\n" + "Detailed output\n" JSON_STR) +{ + struct ospf *ospf; + + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + ospf = ospf_lookup_instance(instance); + if (!ospf || !ospf->oi_running) + return CMD_SUCCESS; + + return show_ip_ospf_neighbor_id_common(vty, ospf, &router_id, !!json, 0, + !!detail, NULL); +} + +static int show_ip_ospf_neighbor_detail_common(struct vty *vty, + struct ospf *ospf, + json_object *json, bool use_json, + uint8_t use_vrf) +{ + struct ospf_interface *oi; + struct listnode *node; + json_object *json_vrf = NULL; + json_object *json_nbr_sub = NULL; + + if (use_json) { + if (use_vrf) + json_vrf = json_object_new_object(); + else + json_vrf = json; + + json_nbr_sub = json_object_new_object(); + } + + if (ospf->instance) { + if (use_json) + json_object_int_add(json, "ospfInstance", + ospf->instance); + else + vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); + } + + ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf); + + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + struct route_node *rn; + struct ospf_neighbor *nbr, *prev_nbr = NULL; + + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + nbr = rn->info; + + if (!nbr) + continue; + + if (nbr != oi->nbr_self) { + if (nbr->state != NSM_Down) { + show_ip_ospf_neighbor_detail_sub( + vty, oi, nbr, prev_nbr, + json_nbr_sub, use_json); + } + } + prev_nbr = nbr; + } + } + + if (use_json) { + json_object_object_add(json_vrf, "neighbors", + json_nbr_sub); + if (use_vrf) + json_object_object_add(json, ospf_get_name(ospf), + json_vrf); + } else + vty_out(vty, "\n"); + + return CMD_SUCCESS; +} + +DEFPY(show_ip_ospf_neighbor_detail, + show_ip_ospf_neighbor_detail_cmd, + "show ip ospf [vrf $vrf_name] neighbor detail [json$json]", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "All VRFs\n" + "Neighbor list\n" + "detail of all neighbors\n" + JSON_STR) +{ + struct ospf *ospf; + struct listnode *node = NULL; + int ret = CMD_SUCCESS; + int inst = 0; + uint8_t use_vrf = 0; + json_object *json_vrf = NULL; + + if (json) + json_vrf = json_object_new_object(); + + /* vrf input is provided could be all or specific vrf*/ + if (vrf_name) { + use_vrf = 1; + if (strmatch(vrf_name, "all")) { + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + ret = show_ip_ospf_neighbor_detail_common( + vty, ospf, json_vrf, !!json, use_vrf); + } + if (json) + vty_json(vty, json_vrf); + + return ret; + } + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + if (ospf == NULL || !ospf->oi_running) { + if (json) + vty_json(vty, json_vrf); + else + vty_out(vty, + "%% OSPF is not enabled in vrf %s\n", + vrf_name); + return CMD_SUCCESS; + } + } else { + /* Display default ospf (instance 0) info */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || !ospf->oi_running) { + if (json) + vty_json(vty, json_vrf); + else + vty_out(vty, "%% OSPF is not enabled\n"); + return CMD_SUCCESS; + } + } + + if (ospf) + ret = show_ip_ospf_neighbor_detail_common(vty, ospf, json_vrf, + !!json, use_vrf); + + if (json) + vty_json(vty, json_vrf); + + return ret; +} + +DEFUN (show_ip_ospf_instance_neighbor_detail, + show_ip_ospf_instance_neighbor_detail_cmd, + "show ip ospf (1-65535) neighbor detail [json]", + SHOW_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + "Neighbor list\n" + "detail of all neighbors\n" + JSON_STR) +{ + int idx_number = 3; + struct ospf *ospf; + unsigned short instance = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; + int ret = CMD_SUCCESS; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + ospf = ospf_lookup_instance(instance); + if (!ospf || !ospf->oi_running) + return CMD_SUCCESS; + + if (uj) + json = json_object_new_object(); + + ret = show_ip_ospf_neighbor_detail_common(vty, ospf, json, uj, 0); + + if (uj) + vty_json(vty, json); + + return ret; +} + +static int show_ip_ospf_neighbor_detail_all_common(struct vty *vty, + struct ospf *ospf, + json_object *json, + bool use_json, + uint8_t use_vrf) +{ + struct listnode *node; + struct ospf_interface *oi; + json_object *json_vrf = NULL; + + if (use_json) { + if (use_vrf) + json_vrf = json_object_new_object(); + else + json_vrf = json; + } + + if (ospf->instance) { + if (use_json) + json_object_int_add(json, "ospfInstance", + ospf->instance); + else + vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); + } + + ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf); + + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + struct route_node *rn; + struct ospf_neighbor *nbr, *prev_nbr = NULL; + struct ospf_nbr_nbma *nbr_nbma; + + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + nbr = rn->info; + + if (!nbr) + continue; + + if (nbr != oi->nbr_self) + if (nbr->state != NSM_Down) + show_ip_ospf_neighbor_detail_sub( + vty, oi, rn->info, prev_nbr, + json_vrf, use_json); + prev_nbr = nbr; + } + + if (oi->type != OSPF_IFTYPE_NBMA) + continue; + + struct listnode *nd; + + for (ALL_LIST_ELEMENTS_RO(oi->nbr_nbma, nd, nbr_nbma)) { + if (nbr_nbma->nbr == NULL || + nbr_nbma->nbr->state == NSM_Down) + show_ip_ospf_nbr_nbma_detail_sub( + vty, oi, nbr_nbma, use_json, json_vrf); + } + } + + if (use_json) { + if (use_vrf) + json_object_object_add(json, ospf_get_name(ospf), + json_vrf); + } else { + vty_out(vty, "\n"); + } + + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_neighbor_detail_all, + show_ip_ospf_neighbor_detail_all_cmd, + "show ip ospf [vrf ] neighbor detail all [json]", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "All VRFs\n" + "Neighbor list\n" + "detail of all neighbors\n" + "include down status neighbor\n" + JSON_STR) +{ + struct ospf *ospf; + bool uj = use_json(argc, argv); + struct listnode *node = NULL; + char *vrf_name = NULL; + bool all_vrf = false; + int ret = CMD_SUCCESS; + int inst = 0; + int idx_vrf = 0; + uint8_t use_vrf = 0; + json_object *json = NULL; + + OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + if (uj) + json = json_object_new_object(); + + /* vrf input is provided could be all or specific vrf*/ + if (vrf_name) { + use_vrf = 1; + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + ret = show_ip_ospf_neighbor_detail_all_common( + vty, ospf, json, uj, use_vrf); + } + + if (uj) + vty_json(vty, json); + + return ret; + } + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + json_object_free(json); + return CMD_SUCCESS; + } + } else { + /* Display default ospf (instance 0) info */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + json_object_free(json); + return CMD_SUCCESS; + } + } + + if (ospf) { + ret = show_ip_ospf_neighbor_detail_all_common(vty, ospf, json, + uj, use_vrf); + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + } + } + + if (uj) + json_object_free(json); + + return ret; +} + +DEFUN (show_ip_ospf_instance_neighbor_detail_all, + show_ip_ospf_instance_neighbor_detail_all_cmd, + "show ip ospf (1-65535) neighbor detail all [json]", + SHOW_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + "Neighbor list\n" + "detail of all neighbors\n" + "include down status neighbor\n" + JSON_STR) +{ + int idx_number = 3; + struct ospf *ospf; + unsigned short instance = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; + int ret = CMD_SUCCESS; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + ospf = ospf_lookup_instance(instance); + if (!ospf || !ospf->oi_running) + return CMD_SUCCESS; + + if (uj) + json = json_object_new_object(); + + ret = show_ip_ospf_neighbor_detail_all_common(vty, ospf, json, uj, 0); + + if (uj) + vty_json(vty, json); + + return ret; +} + +static int show_ip_ospf_neighbor_int_detail_common(struct vty *vty, + struct ospf *ospf, + const char *ifname, + bool use_json, + json_object *json_vrf) +{ + struct ospf_interface *oi; + struct interface *ifp; + struct route_node *rn, *nrn; + struct ospf_neighbor *nbr; + json_object *json = NULL; + + if (use_json) { + json = json_object_new_object(); + if (json_vrf) + json_object_object_add(json_vrf, + (ospf->vrf_id == VRF_DEFAULT) + ? "default" + : ospf->name, + json); + } + + if (ospf->instance) { + if (use_json) + json_object_int_add(json, "ospfInstance", + ospf->instance); + else + vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); + } + + ifp = if_lookup_by_name(ifname, ospf->vrf_id); + if (!ifp) { + if (!use_json) { + vty_out(vty, "No such interface.\n"); + } else { + if (!json_vrf) + vty_json(vty, json); + } + return CMD_WARNING; + } + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + oi = rn->info; + + if (!oi) + continue; + + for (nrn = route_top(oi->nbrs); nrn; nrn = route_next(nrn)) { + nbr = nrn->info; + + if (!nbr) + continue; + + if (nbr == oi->nbr_self) + continue; + + if (nbr->state == NSM_Down) + continue; + + show_ip_ospf_neighbor_detail_sub(vty, oi, nbr, NULL, + json, use_json); + } + } + + if (use_json) { + if (!json_vrf) + vty_json(vty, json); + } else { + vty_out(vty, "\n"); + } + + return CMD_SUCCESS; +} + +DEFPY(show_ip_ospf_neighbor_int, + show_ip_ospf_neighbor_int_cmd, + "show ip ospf [vrf NAME$vrf_name] neighbor IFNAME$ifname [json$json]", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "Neighbor list\n" + "Interface name\n" + JSON_STR) +{ + struct ospf *ospf; + int ret = CMD_SUCCESS; + struct interface *ifp = NULL; + vrf_id_t vrf_id = VRF_DEFAULT; + struct vrf *vrf = NULL; + + if (vrf_name && strmatch(vrf_name, VRF_DEFAULT_NAME)) + vrf_name = NULL; + if (vrf_name) { + vrf = vrf_lookup_by_name(vrf_name); + if (vrf) + vrf_id = vrf->vrf_id; + } + ospf = ospf_lookup_by_vrf_id(vrf_id); + + if (!ospf || !ospf->oi_running) { + if (json) + vty_json_empty(vty); + return ret; + } + + if (!json) + show_ip_ospf_neighbour_header(vty); + + ifp = if_lookup_by_name(ifname, vrf_id); + if (!ifp) { + if (json) + vty_json_empty(vty); + else + vty_out(vty, "No such interface.\n"); + return ret; + } + + ret = show_ip_ospf_neighbor_int_common(vty, ospf, ifname, !!json, 0); + return ret; +} + +DEFPY(show_ip_ospf_neighbor_int_detail, + show_ip_ospf_neighbor_int_detail_cmd, + "show ip ospf [vrf NAME$vrf_name] neighbor IFNAME$ifname detail [json$json]", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "Neighbor list\n" + "Interface name\n" + "detail of all neighbors\n" + JSON_STR) +{ + struct ospf *ospf; + struct listnode *node = NULL; + int ret = CMD_SUCCESS; + bool ospf_output = false; + + if (vrf_name && !strmatch(vrf_name, "all")) { + int inst = 0; + + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + if (ospf == NULL || !ospf->oi_running) { + if (!json) + vty_out(vty, + "%% OSPF is not enabled in vrf %s\n", + vrf_name); + else + vty_json_empty(vty); + return CMD_SUCCESS; + } + return show_ip_ospf_neighbor_int_detail_common( + vty, ospf, ifname, !!json, NULL); + } + + json_object *json_vrf = NULL; + + if (json) + json_vrf = json_object_new_object(); + + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + ospf_output = true; + ret = show_ip_ospf_neighbor_int_detail_common(vty, ospf, ifname, + !!json, json_vrf); + } + + if (json) { + vty_json(vty, json_vrf); + return ret; + } + + if (!ospf_output) + vty_out(vty, "%% OSPF instance not found\n"); + + return ret; +} + +DEFPY(show_ip_ospf_instance_neighbor_int_detail, + show_ip_ospf_instance_neighbor_int_detail_cmd, + "show ip ospf (1-65535)$instance neighbor IFNAME$ifname detail [json$json]", + SHOW_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + "Neighbor list\n" + "Interface name\n" + "detail of all neighbors\n" + JSON_STR) +{ + struct ospf *ospf; + + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + ospf = ospf_lookup_instance(instance); + if (!ospf || !ospf->oi_running) + return CMD_SUCCESS; + + return show_ip_ospf_neighbor_int_detail_common(vty, ospf, ifname, + !!json, NULL); +} + +/* Show functions */ +static int show_lsa_summary(struct vty *vty, struct ospf_lsa *lsa, int self, + json_object *json_lsa) +{ + struct router_lsa *rl; + struct summary_lsa *sl; + struct as_external_lsa *asel; + struct prefix_ipv4 p; + + if (lsa == NULL) + return 0; + + /* If self option is set, check LSA self flag. */ + if (self == 0 || IS_LSA_SELF(lsa)) { + + if (!json_lsa) { + /* LSA common part show. */ + vty_out(vty, "%-15pI4", &lsa->data->id); + vty_out(vty, "%-15pI4 %4d 0x%08lx 0x%04x", + &lsa->data->adv_router, LS_AGE(lsa), + (unsigned long)ntohl(lsa->data->ls_seqnum), + ntohs(lsa->data->checksum)); + } else { + char seqnum[10]; + char checksum[10]; + + snprintf(seqnum, sizeof(seqnum), "%x", + ntohl(lsa->data->ls_seqnum)); + snprintf(checksum, sizeof(checksum), "%x", + ntohs(lsa->data->checksum)); + json_object_string_addf(json_lsa, "lsId", "%pI4", + &lsa->data->id); + json_object_string_addf(json_lsa, "advertisedRouter", + "%pI4", &lsa->data->adv_router); + json_object_int_add(json_lsa, "lsaAge", LS_AGE(lsa)); + json_object_string_add(json_lsa, "sequenceNumber", + seqnum); + json_object_string_add(json_lsa, "checksum", checksum); + } + + /* LSA specific part show. */ + switch (lsa->data->type) { + case OSPF_ROUTER_LSA: + rl = (struct router_lsa *)lsa->data; + + if (!json_lsa) + vty_out(vty, " %-d", ntohs(rl->links)); + else + json_object_int_add(json_lsa, + "numOfRouterLinks", + ntohs(rl->links)); + break; + case OSPF_SUMMARY_LSA: + sl = (struct summary_lsa *)lsa->data; + + p.family = AF_INET; + p.prefix = sl->header.id; + p.prefixlen = ip_masklen(sl->mask); + apply_mask_ipv4(&p); + + if (!json_lsa) + vty_out(vty, " %pFX", &p); + else { + json_object_string_addf( + json_lsa, "summaryAddress", "%pFX", &p); + } + break; + case OSPF_AS_EXTERNAL_LSA: + case OSPF_AS_NSSA_LSA: + asel = (struct as_external_lsa *)lsa->data; + + p.family = AF_INET; + p.prefix = asel->header.id; + p.prefixlen = ip_masklen(asel->mask); + apply_mask_ipv4(&p); + + if (!json_lsa) + vty_out(vty, " %s %pFX [0x%lx]", + IS_EXTERNAL_METRIC(asel->e[0].tos) + ? "E2" + : "E1", + &p, + (unsigned long)ntohl( + asel->e[0].route_tag)); + else { + json_object_string_add( + json_lsa, "metricType", + IS_EXTERNAL_METRIC(asel->e[0].tos) + ? "E2" + : "E1"); + json_object_string_addf(json_lsa, "route", + "%pFX", &p); + json_object_int_add( + json_lsa, "tag", + (unsigned long)ntohl( + asel->e[0].route_tag)); + } + break; + case OSPF_NETWORK_LSA: + case OSPF_ASBR_SUMMARY_LSA: + case OSPF_OPAQUE_LINK_LSA: + case OSPF_OPAQUE_AREA_LSA: + case OSPF_OPAQUE_AS_LSA: + default: + break; + } + + if (!json_lsa) + vty_out(vty, "\n"); + } + + return 1; +} + +static const char *const show_database_desc[] = { + "unknown", + "Router Link States", + "Net Link States", + "Summary Link States", + "ASBR-Summary Link States", + "AS External Link States", + "Group Membership LSA", + "NSSA-external Link States", + "Type-8 LSA", + "Link-Local Opaque-LSA", + "Area-Local Opaque-LSA", + "AS-external Opaque-LSA", +}; + +static const char * const show_database_desc_json[] = { + "unknown", + "routerLinkStates", + "networkLinkStates", + "summaryLinkStates", + "asbrSummaryLinkStates", + "asExternalLinkStates", + "groupMembershipLsa", + "nssaExternalLinkStates", + "type8Lsa", + "linkLocalOpaqueLsa", + "areaLocalOpaqueLsa", + "asExternalOpaqueLsa", +}; + +static const char *const show_database_desc_count_json[] = { + "unknownCount", + "routerLinkStatesCount", + "networkLinkStatesCount", + "summaryLinkStatesCount", + "asbrSummaryLinkStatesCount", + "asExternalLinkStatesCount", + "groupMembershipLsaCount", + "nssaExternalLinkStatesCount", + "type8LsaCount", + "linkLocalOpaqueLsaCount", + "areaLocalOpaqueLsaCount", + "asExternalOpaqueLsaCount", +}; + +static const char *const show_database_header[] = { + "", + "Link ID ADV Router Age Seq# CkSum Link count", + "Link ID ADV Router Age Seq# CkSum", + "Link ID ADV Router Age Seq# CkSum Route", + "Link ID ADV Router Age Seq# CkSum", + "Link ID ADV Router Age Seq# CkSum Route", + " --- header for Group Member ----", + "Link ID ADV Router Age Seq# CkSum Route", + " --- type-8 ---", + "Opaque-Type/Id ADV Router Age Seq# CkSum", + "Opaque-Type/Id ADV Router Age Seq# CkSum", + "Opaque-Type/Id ADV Router Age Seq# CkSum", +}; + +static void show_ip_ospf_database_header(struct vty *vty, struct ospf_lsa *lsa, + json_object *json) +{ + struct router_lsa *rlsa = (struct router_lsa *)lsa->data; + + if (!json) { + if (IS_LSA_SELF(lsa)) + vty_out(vty, " LS age: %d%s\n", LS_AGE(lsa), + CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE) + ? "(S-DNA)" + : ""); + else + vty_out(vty, " LS age: %d%s\n", LS_AGE(lsa), + CHECK_FLAG(lsa->data->ls_age, DO_NOT_AGE) + ? "(DNA)" + : ""); + vty_out(vty, " Options: 0x%-2x : %s\n", lsa->data->options, + ospf_options_dump(lsa->data->options)); + vty_out(vty, " LS Flags: 0x%-2x %s\n", lsa->flags, + ((lsa->flags & OSPF_LSA_LOCAL_XLT) + ? "(Translated from Type-7)" + : "")); + + if (lsa->data->type == OSPF_ROUTER_LSA) { + vty_out(vty, " Flags: 0x%x", rlsa->flags); + + if (rlsa->flags) + vty_out(vty, " :%s%s%s%s", + IS_ROUTER_LSA_BORDER(rlsa) ? " ABR" + : "", + IS_ROUTER_LSA_EXTERNAL(rlsa) ? " ASBR" + : "", + IS_ROUTER_LSA_VIRTUAL(rlsa) + ? " VL-endpoint" + : "", + IS_ROUTER_LSA_SHORTCUT(rlsa) + ? " Shortcut" + : ""); + + vty_out(vty, "\n"); + } + vty_out(vty, " LS Type: %s\n", + lookup_msg(ospf_lsa_type_msg, lsa->data->type, NULL)); + vty_out(vty, " Link State ID: %pI4 %s\n", + &lsa->data->id, + lookup_msg(ospf_link_state_id_type_msg, lsa->data->type, + NULL)); + vty_out(vty, " Advertising Router: %pI4\n", + &lsa->data->adv_router); + vty_out(vty, " LS Seq Number: %08lx\n", + (unsigned long)ntohl(lsa->data->ls_seqnum)); + vty_out(vty, " Checksum: 0x%04x\n", + ntohs(lsa->data->checksum)); + vty_out(vty, " Length: %d\n\n", ntohs(lsa->data->length)); + } else { + char seqnum[10]; + char checksum[10]; + + snprintf(seqnum, 10, "%x", ntohl(lsa->data->ls_seqnum)); + snprintf(checksum, 10, "%x", ntohs(lsa->data->checksum)); + + json_object_int_add(json, "lsaAge", LS_AGE(lsa)); + json_object_string_add(json, "options", + ospf_options_dump(lsa->data->options)); + json_object_int_add(json, "lsaFlags", lsa->flags); + + if (lsa->flags & OSPF_LSA_LOCAL_XLT) + json_object_boolean_true_add(json, + "translatedFromType7"); + + if (lsa->data->type == OSPF_ROUTER_LSA) { + json_object_int_add(json, "flags", rlsa->flags); + + if (rlsa->flags) { + if (IS_ROUTER_LSA_BORDER(rlsa)) + json_object_boolean_true_add(json, + "abr"); + if (IS_ROUTER_LSA_EXTERNAL(rlsa)) + json_object_boolean_true_add(json, + "asbr"); + if (IS_ROUTER_LSA_VIRTUAL(rlsa)) + json_object_boolean_true_add( + json, "vlEndpoint"); + if (IS_ROUTER_LSA_SHORTCUT(rlsa)) + json_object_boolean_true_add( + json, "shortcut"); + } + } + + json_object_string_add( + json, "lsaType", + lookup_msg(ospf_lsa_type_msg, lsa->data->type, NULL)); + json_object_string_addf(json, "linkStateId", "%pI4", + &lsa->data->id); + json_object_string_addf(json, "advertisingRouter", "%pI4", + &lsa->data->adv_router); + json_object_string_add(json, "lsaSeqNumber", seqnum); + json_object_string_add(json, "checksum", checksum); + json_object_int_add(json, "length", ntohs(lsa->data->length)); + } +} + +static const char *const link_type_desc[] = { + "(null)", + "another Router (point-to-point)", + "a Transit Network", + "Stub Network", + "a Virtual Link", +}; + +static const char *const link_id_desc[] = { + "(null)", "Neighboring Router ID", "Designated Router address", + "Net", "Neighboring Router ID", +}; + +static const char *const link_data_desc[] = { + "(null)", "Router Interface address", "Router Interface address", + "Network Mask", "Router Interface address", +}; + +static const char *const link_id_desc_json[] = { + "null", "neighborRouterId", "designatedRouterAddress", + "networkAddress", "neighborRouterId", +}; + +static const char *const link_data_desc_json[] = { + "null", "routerInterfaceAddress", "routerInterfaceAddress", + "networkMask", "routerInterfaceAddress", +}; + +/* Show router-LSA each Link information. */ +static void show_ip_ospf_database_router_links(struct vty *vty, + struct router_lsa *rl, + json_object *json) +{ + int len, type; + unsigned short i; + json_object *json_links = NULL; + json_object *json_link = NULL; + int metric = 0; + char buf[PREFIX_STRLEN]; + + if (json) + json_links = json_object_new_object(); + + len = ntohs(rl->header.length) - 4; + for (i = 0; i < ntohs(rl->links) && len > 0; len -= 12, i++) { + type = rl->link[i].type; + + if (json) { + char link[16]; + + snprintf(link, sizeof(link), "link%u", i); + json_link = json_object_new_object(); + json_object_string_add(json_link, "linkType", + link_type_desc[type]); + json_object_string_add(json_link, + link_id_desc_json[type], + inet_ntop(AF_INET, + &rl->link[i].link_id, + buf, sizeof(buf))); + json_object_string_add( + json_link, link_data_desc_json[type], + inet_ntop(AF_INET, &rl->link[i].link_data, + buf, sizeof(buf))); + json_object_int_add(json_link, "numOfTosMetrics", + metric); + json_object_int_add(json_link, "tos0Metric", + ntohs(rl->link[i].metric)); + json_object_object_add(json_links, link, json_link); + } else { + vty_out(vty, " Link connected to: %s\n", + link_type_desc[type]); + vty_out(vty, " (Link ID) %s: %pI4\n", + link_id_desc[type], + &rl->link[i].link_id); + vty_out(vty, " (Link Data) %s: %pI4\n", + link_data_desc[type], + &rl->link[i].link_data); + vty_out(vty, " Number of TOS metrics: 0\n"); + vty_out(vty, " TOS 0 Metric: %d\n", + ntohs(rl->link[i].metric)); + vty_out(vty, "\n"); + } + } + if (json) + json_object_object_add(json, "routerLinks", json_links); +} + +/* Show router-LSA detail information. */ +static int show_router_lsa_detail(struct vty *vty, struct ospf_lsa *lsa, + json_object *json) +{ + if (lsa != NULL) { + struct router_lsa *rl = (struct router_lsa *)lsa->data; + + show_ip_ospf_database_header(vty, lsa, json); + + if (!json) + vty_out(vty, " Number of Links: %d\n\n", + ntohs(rl->links)); + else + json_object_int_add(json, "numOfLinks", + ntohs(rl->links)); + + show_ip_ospf_database_router_links(vty, rl, json); + + if (!json) + vty_out(vty, "\n"); + } + + return 0; +} + +/* Show network-LSA detail information. */ +static int show_network_lsa_detail(struct vty *vty, struct ospf_lsa *lsa, + json_object *json) +{ + int length, i; + char buf[PREFIX_STRLEN]; + json_object *json_attached_rt = NULL; + json_object *json_router = NULL; + + if (json) + json_attached_rt = json_object_new_object(); + + if (lsa != NULL) { + struct network_lsa *nl = (struct network_lsa *)lsa->data; + struct in_addr *addr; + + show_ip_ospf_database_header(vty, lsa, json); + + if (!json) + vty_out(vty, " Network Mask: /%d\n", + ip_masklen(nl->mask)); + else + json_object_int_add(json, "networkMask", + ip_masklen(nl->mask)); + + length = lsa->size - OSPF_LSA_HEADER_SIZE - 4; + addr = &nl->routers[0]; + for (i = 0; length > 0 && addr; + length -= 4, addr = &nl->routers[++i]) + if (!json) { + vty_out(vty, " Attached Router: %pI4\n", + addr); + vty_out(vty, "\n"); + } else { + json_router = json_object_new_object(); + json_object_string_add( + json_router, "attachedRouterId", + inet_ntop(AF_INET, addr, buf, + sizeof(buf))); + json_object_object_add(json_attached_rt, + inet_ntop(AF_INET, addr, + buf, + sizeof(buf)), + json_router); + } + } + + if (json) + json_object_object_add(json, "attchedRouters", + json_attached_rt); + + return 0; +} + +/* Show summary-LSA detail information. */ +static int show_summary_lsa_detail(struct vty *vty, struct ospf_lsa *lsa, + json_object *json) +{ + if (lsa != NULL) { + struct summary_lsa *sl = (struct summary_lsa *)lsa->data; + + show_ip_ospf_database_header(vty, lsa, json); + + if (!json) { + vty_out(vty, " Network Mask: /%d\n", + ip_masklen(sl->mask)); + vty_out(vty, " TOS: 0 Metric: %d\n", + GET_METRIC(sl->metric)); + vty_out(vty, "\n"); + } else { + json_object_int_add(json, "networkMask", + ip_masklen(sl->mask)); + json_object_int_add(json, "tos0Metric", + GET_METRIC(sl->metric)); + } + } + + return 0; +} + +/* Show summary-ASBR-LSA detail information. */ +static int show_summary_asbr_lsa_detail(struct vty *vty, struct ospf_lsa *lsa, + json_object *json) +{ + if (lsa != NULL) { + struct summary_lsa *sl = (struct summary_lsa *)lsa->data; + + show_ip_ospf_database_header(vty, lsa, json); + + if (!json) { + vty_out(vty, " Network Mask: /%d\n", + ip_masklen(sl->mask)); + vty_out(vty, " TOS: 0 Metric: %d\n", + GET_METRIC(sl->metric)); + vty_out(vty, "\n"); + } else { + json_object_int_add(json, "networkMask", + ip_masklen(sl->mask)); + json_object_int_add(json, "tos0Metric", + GET_METRIC(sl->metric)); + } + } + + return 0; +} + +/* Show AS-external-LSA detail information. */ +static int show_as_external_lsa_detail(struct vty *vty, struct ospf_lsa *lsa, + json_object *json) +{ + int tos = 0; + + if (lsa != NULL) { + struct as_external_lsa *al = + (struct as_external_lsa *)lsa->data; + + show_ip_ospf_database_header(vty, lsa, json); + + if (!json) { + vty_out(vty, " Network Mask: /%d\n", + ip_masklen(al->mask)); + vty_out(vty, " Metric Type: %s\n", + IS_EXTERNAL_METRIC(al->e[0].tos) + ? "2 (Larger than any link state path)" + : "1"); + vty_out(vty, " TOS: 0\n"); + vty_out(vty, " Metric: %d\n", + GET_METRIC(al->e[0].metric)); + vty_out(vty, " Forward Address: %pI4\n", + &al->e[0].fwd_addr); + vty_out(vty, + " External Route Tag: %" ROUTE_TAG_PRI "\n\n", + (route_tag_t)ntohl(al->e[0].route_tag)); + } else { + json_object_int_add(json, "networkMask", + ip_masklen(al->mask)); + json_object_string_add( + json, "metricType", + IS_EXTERNAL_METRIC(al->e[0].tos) + ? "E2 (Larger than any link state path)" + : "E1"); + json_object_int_add(json, "tos", tos); + json_object_int_add(json, "metric", + GET_METRIC(al->e[0].metric)); + json_object_string_addf(json, "forwardAddress", "%pI4", + &(al->e[0].fwd_addr)); + json_object_int_add( + json, "externalRouteTag", + (route_tag_t)ntohl(al->e[0].route_tag)); + } + } + + return 0; +} + +/* Show AS-NSSA-LSA detail information. */ +static int show_as_nssa_lsa_detail(struct vty *vty, struct ospf_lsa *lsa, + json_object *json) +{ + int tos = 0; + + if (lsa != NULL) { + struct as_external_lsa *al = + (struct as_external_lsa *)lsa->data; + + show_ip_ospf_database_header(vty, lsa, json); + + if (!json) { + vty_out(vty, " Network Mask: /%d\n", + ip_masklen(al->mask)); + vty_out(vty, " Metric Type: %s\n", + IS_EXTERNAL_METRIC(al->e[0].tos) + ? "2 (Larger than any link state path)" + : "1"); + vty_out(vty, " TOS: 0\n"); + vty_out(vty, " Metric: %d\n", + GET_METRIC(al->e[0].metric)); + vty_out(vty, " NSSA: Forward Address: %pI4\n", + &al->e[0].fwd_addr); + vty_out(vty, + " External Route Tag: %" ROUTE_TAG_PRI + "\n\n", + (route_tag_t)ntohl(al->e[0].route_tag)); + } else { + json_object_int_add(json, "networkMask", + ip_masklen(al->mask)); + json_object_string_add( + json, "metricType", + IS_EXTERNAL_METRIC(al->e[0].tos) + ? "E2 (Larger than any link state path)" + : "E1"); + json_object_int_add(json, "tos", tos); + json_object_int_add(json, "metric", + GET_METRIC(al->e[0].metric)); + json_object_string_addf(json, "nssaForwardAddress", + "%pI4", &al->e[0].fwd_addr); + json_object_int_add( + json, "externalRouteTag", + (route_tag_t)ntohl(al->e[0].route_tag)); + } + } + + return 0; +} + +static int show_func_dummy(struct vty *vty, struct ospf_lsa *lsa, + json_object *json) +{ + return 0; +} + +static int show_opaque_lsa_detail(struct vty *vty, struct ospf_lsa *lsa, + json_object *json) +{ + if (lsa != NULL) { + show_ip_ospf_database_header(vty, lsa, json); + show_opaque_info_detail(vty, lsa, json); + if (!json) + vty_out(vty, "\n"); + } + return 0; +} + +int (*show_function[])(struct vty *, struct ospf_lsa *, json_object *) = { + NULL, + show_router_lsa_detail, + show_network_lsa_detail, + show_summary_lsa_detail, + show_summary_asbr_lsa_detail, + show_as_external_lsa_detail, + show_func_dummy, + show_as_nssa_lsa_detail, /* almost same as external */ + NULL, /* type-8 */ + show_opaque_lsa_detail, + show_opaque_lsa_detail, + show_opaque_lsa_detail, +}; + +static void show_lsa_prefix_set(struct vty *vty, struct prefix_ls *lp, + struct in_addr *id, struct in_addr *adv_router) +{ + memset(lp, 0, sizeof(struct prefix_ls)); + lp->family = AF_UNSPEC; + if (id == NULL) + lp->prefixlen = 0; + else if (adv_router == NULL) { + lp->prefixlen = IPV4_MAX_BITLEN; + lp->id = *id; + } else { + lp->prefixlen = 64; + lp->id = *id; + lp->adv_router = *adv_router; + } +} + +static void show_lsa_detail_proc(struct vty *vty, struct route_table *rt, + struct in_addr *id, struct in_addr *adv_router, + json_object *json) +{ + struct prefix_ls lp; + struct route_node *rn, *start; + struct ospf_lsa *lsa; + json_object *json_lsa = NULL; + + show_lsa_prefix_set(vty, &lp, id, adv_router); + start = route_node_get(rt, (struct prefix *)&lp); + if (start) { + route_lock_node(start); + for (rn = start; rn; rn = route_next_until(rn, start)) + if ((lsa = rn->info)) { + if (show_function[lsa->data->type] != NULL) { + if (json) { + json_lsa = + json_object_new_object(); + json_object_array_add(json, + json_lsa); + } + + show_function[lsa->data->type]( + vty, lsa, json_lsa); + } + } + route_unlock_node(start); + } +} + +/* Show detail LSA information + -- if id is NULL then show all LSAs. */ +static void show_lsa_detail(struct vty *vty, struct ospf *ospf, int type, + struct in_addr *id, struct in_addr *adv_router, + json_object *json) +{ + struct listnode *node; + struct ospf_area *area; + char buf[PREFIX_STRLEN]; + json_object *json_lsa_type = NULL; + json_object *json_areas = NULL; + json_object *json_lsa_array = NULL; + + switch (type) { + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + if (!json) + vty_out(vty, " %s \n\n", + show_database_desc[type]); + else + json_lsa_array = json_object_new_array(); + + show_lsa_detail_proc(vty, AS_LSDB(ospf, type), id, adv_router, + json_lsa_array); + if (json) + json_object_object_add(json, + show_database_desc_json[type], + json_lsa_array); + + break; + default: + if (json) + json_areas = json_object_new_object(); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (!json) { + vty_out(vty, + "\n %s (Area %s)\n\n", + show_database_desc[type], + ospf_area_desc_string(area)); + } else { + json_lsa_array = json_object_new_array(); + json_object_object_add(json_areas, + inet_ntop(AF_INET, + &area->area_id, + buf, + sizeof(buf)), + json_lsa_array); + } + + show_lsa_detail_proc(vty, AREA_LSDB(area, type), id, + adv_router, json_lsa_array); + } + + if (json) { + json_lsa_type = json_object_new_object(); + json_object_object_add(json_lsa_type, "areas", + json_areas); + json_object_object_add(json, + show_database_desc_json[type], + json_lsa_type); + } + break; + } +} + +static void show_lsa_detail_adv_router_proc(struct vty *vty, + struct route_table *rt, + struct in_addr *adv_router, + json_object *json) +{ + struct route_node *rn; + struct ospf_lsa *lsa; + json_object *json_lsa = NULL; + + for (rn = route_top(rt); rn; rn = route_next(rn)) + if ((lsa = rn->info)) { + if (IPV4_ADDR_SAME(adv_router, + &lsa->data->adv_router)) { + if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT)) + continue; + if (json) { + json_lsa = json_object_new_object(); + json_object_array_add(json, json_lsa); + } + + if (show_function[lsa->data->type] != NULL) + show_function[lsa->data->type]( + vty, lsa, json_lsa); + } + } +} + +/* Show detail LSA information. */ +static void show_lsa_detail_adv_router(struct vty *vty, struct ospf *ospf, + int type, struct in_addr *adv_router, + json_object *json) +{ + struct listnode *node; + struct ospf_area *area; + char buf[PREFIX_STRLEN]; + json_object *json_lsa_type = NULL; + json_object *json_areas = NULL; + json_object *json_lsa_array = NULL; + + if (json) + json_lsa_type = json_object_new_object(); + + switch (type) { + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + if (!json) + vty_out(vty, " %s \n\n", + show_database_desc[type]); + else + json_lsa_array = json_object_new_array(); + + show_lsa_detail_adv_router_proc(vty, AS_LSDB(ospf, type), + adv_router, json_lsa_array); + if (json) + json_object_object_add(json, + show_database_desc_json[type], + json_lsa_array); + break; + default: + if (json) + json_areas = json_object_new_object(); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (!json) { + vty_out(vty, + "\n %s (Area %s)\n\n", + show_database_desc[type], + ospf_area_desc_string(area)); + } else { + json_lsa_array = json_object_new_array(); + json_object_object_add( + json_areas, + inet_ntop(AF_INET, &area->area_id, buf, + sizeof(buf)), + json_lsa_array); + } + + show_lsa_detail_adv_router_proc( + vty, AREA_LSDB(area, type), adv_router, + json_lsa_array); + } + + if (json) { + json_object_object_add(json_lsa_type, "areas", + json_areas); + json_object_object_add(json, + show_database_desc_json[type], + json_lsa_type); + } + break; + } +} + +void show_ip_ospf_database_summary(struct vty *vty, struct ospf *ospf, int self, + json_object *json) +{ + struct ospf_lsa *lsa; + struct route_node *rn; + struct ospf_area *area; + struct listnode *node; + char buf[PREFIX_STRLEN]; + json_object *json_areas = NULL; + json_object *json_area = NULL; + json_object *json_lsa = NULL; + int type; + json_object *json_lsa_array = NULL; + uint32_t count; + + if (json) + json_areas = json_object_new_object(); + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (json) + json_area = json_object_new_object(); + + for (type = OSPF_MIN_LSA; type < OSPF_MAX_LSA; type++) { + count = 0; + switch (type) { + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + continue; + default: + break; + } + if (ospf_lsdb_count_self(area->lsdb, type) > 0 + || (!self + && ospf_lsdb_count(area->lsdb, type) > 0)) { + + if (!json) { + vty_out(vty, + " %s (Area %s)\n\n", + show_database_desc[type], + ospf_area_desc_string(area)); + vty_out(vty, "%s\n", + show_database_header[type]); + } else { + json_lsa_array = + json_object_new_array(); + json_object_object_add( + json_area, + show_database_desc_json[type], + json_lsa_array); + } + + LSDB_LOOP (AREA_LSDB(area, type), rn, lsa) { + if (json) { + json_lsa = + json_object_new_object(); + json_object_array_add( + json_lsa_array, + json_lsa); + } + + count += show_lsa_summary( + vty, lsa, self, json_lsa); + } + + if (!json) + vty_out(vty, "\n"); + else + json_object_int_add( + json_area, + + show_database_desc_count_json + [type], + count); + } + } + if (json) + json_object_object_add(json_areas, + inet_ntop(AF_INET, + &area->area_id, + buf, sizeof(buf)), + json_area); + } + + if (json) + json_object_object_add(json, "areas", json_areas); + + for (type = OSPF_MIN_LSA; type < OSPF_MAX_LSA; type++) { + count = 0; + switch (type) { + case OSPF_AS_EXTERNAL_LSA: + case OSPF_OPAQUE_AS_LSA: + break; + default: + continue; + } + if (ospf_lsdb_count_self(ospf->lsdb, type) + || (!self && ospf_lsdb_count(ospf->lsdb, type))) { + if (!json) { + vty_out(vty, " %s\n\n", + show_database_desc[type]); + vty_out(vty, "%s\n", + show_database_header[type]); + } else { + json_lsa_array = json_object_new_array(); + json_object_object_add( + json, show_database_desc_json[type], + json_lsa_array); + } + + LSDB_LOOP (AS_LSDB(ospf, type), rn, lsa) { + if (json) { + json_lsa = json_object_new_object(); + json_object_array_add(json_lsa_array, + json_lsa); + } + + count += show_lsa_summary(vty, lsa, self, + json_lsa); + } + + if (!json) + vty_out(vty, "\n"); + else + json_object_int_add( + json, + show_database_desc_count_json[type], + count); + } + } + + if (!json) + vty_out(vty, "\n"); +} + +static void show_ip_ospf_database_maxage(struct vty *vty, struct ospf *ospf, + json_object *json) +{ + struct route_node *rn; + char buf[PREFIX_STRLEN]; + json_object *json_maxage = NULL; + + if (!json) + vty_out(vty, "\n MaxAge Link States:\n\n"); + else + json_maxage = json_object_new_object(); + + for (rn = route_top(ospf->maxage_lsa); rn; rn = route_next(rn)) { + struct ospf_lsa *lsa; + json_object *json_lsa = NULL; + + if ((lsa = rn->info) != NULL) { + if (!json) { + vty_out(vty, "Link type: %d\n", + lsa->data->type); + vty_out(vty, "Link State ID: %pI4\n", + &lsa->data->id); + vty_out(vty, "Advertising Router: %pI4\n", + &lsa->data->adv_router); + vty_out(vty, "LSA lock count: %d\n", lsa->lock); + vty_out(vty, "\n"); + } else { + json_lsa = json_object_new_object(); + json_object_int_add(json_lsa, "linkType", + lsa->data->type); + json_object_string_addf(json_lsa, "linkStateId", + "%pI4", &lsa->data->id); + json_object_string_addf( + json_lsa, "advertisingRouter", "%pI4", + &lsa->data->adv_router); + json_object_int_add(json_lsa, "lsaLockCount", + lsa->lock); + json_object_object_add( + json_maxage, + inet_ntop(AF_INET, + &lsa->data->id, + buf, sizeof(buf)), + json_lsa); + } + } + } + if (json) + json_object_object_add(json, "maxAgeLinkStates", json_maxage); +} + +#define OSPF_LSA_TYPE_NSSA_DESC "NSSA external link state\n" +#define OSPF_LSA_TYPE_NSSA_CMD_STR "|nssa-external" + +#define OSPF_LSA_TYPE_OPAQUE_LINK_DESC "Link local Opaque-LSA\n" +#define OSPF_LSA_TYPE_OPAQUE_AREA_DESC "Link area Opaque-LSA\n" +#define OSPF_LSA_TYPE_OPAQUE_AS_DESC "Link AS Opaque-LSA\n" +#define OSPF_LSA_TYPE_OPAQUE_CMD_STR "|opaque-link|opaque-area|opaque-as" + +#define OSPF_LSA_TYPES_DESC \ + "ASBR summary link states\n" \ + "External link states\n" \ + "Network link states\n" \ + "Router link states\n" \ + "Network summary link states\n" OSPF_LSA_TYPE_NSSA_DESC \ + OSPF_LSA_TYPE_OPAQUE_LINK_DESC OSPF_LSA_TYPE_OPAQUE_AREA_DESC \ + OSPF_LSA_TYPE_OPAQUE_AS_DESC + +static int +show_ip_ospf_database_common(struct vty *vty, struct ospf *ospf, bool maxage, + bool self, bool detail, const char *type_name, + struct in_addr *lsid, struct in_addr *adv_router, + bool use_vrf, json_object *json, bool uj) +{ + int type; + json_object *json_vrf = NULL; + + if (uj) { + if (use_vrf) + json_vrf = json_object_new_object(); + else + json_vrf = json; + } + + if (ospf->instance) { + if (uj) + json_object_int_add(json_vrf, "ospfInstance", + ospf->instance); + else + vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); + } + + ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf); + + /* Show Router ID. */ + if (uj) { + json_object_string_addf(json_vrf, "routerId", "%pI4", + &ospf->router_id); + } else { + vty_out(vty, "\n OSPF Router with ID (%pI4)\n\n", + &ospf->router_id); + } + + /* Show MaxAge LSAs */ + if (maxage) { + show_ip_ospf_database_maxage(vty, ospf, json_vrf); + if (json) { + if (use_vrf) { + if (ospf->vrf_id == VRF_DEFAULT) + json_object_object_add(json, "default", + json_vrf); + else + json_object_object_add(json, ospf->name, + json_vrf); + } + } + return CMD_SUCCESS; + } + + /* Show all LSAs. */ + if (!type_name) { + if (detail) { + for (int i = OSPF_ROUTER_LSA; i <= OSPF_OPAQUE_AS_LSA; + i++) { + switch (i) { + case OSPF_GROUP_MEMBER_LSA: + case OSPF_EXTERNAL_ATTRIBUTES_LSA: + /* ignore deprecated LSA types */ + continue; + default: + break; + } + + if (adv_router && !lsid) + show_lsa_detail_adv_router(vty, ospf, i, + adv_router, + json_vrf); + else + show_lsa_detail(vty, ospf, i, lsid, + adv_router, json_vrf); + } + } else + show_ip_ospf_database_summary(vty, ospf, self, + json_vrf); + + if (json) { + if (use_vrf) { + if (ospf->vrf_id == VRF_DEFAULT) + json_object_object_add(json, "default", + json_vrf); + else + json_object_object_add(json, ospf->name, + json_vrf); + } + } + return CMD_SUCCESS; + } + + /* Set database type to show. */ + if (strncmp(type_name, "r", 1) == 0) + type = OSPF_ROUTER_LSA; + else if (strncmp(type_name, "ne", 2) == 0) + type = OSPF_NETWORK_LSA; + else if (strncmp(type_name, "ns", 2) == 0) + type = OSPF_AS_NSSA_LSA; + else if (strncmp(type_name, "su", 2) == 0) + type = OSPF_SUMMARY_LSA; + else if (strncmp(type_name, "a", 1) == 0) + type = OSPF_ASBR_SUMMARY_LSA; + else if (strncmp(type_name, "e", 1) == 0) + type = OSPF_AS_EXTERNAL_LSA; + else if (strncmp(type_name, "opaque-l", 8) == 0) + type = OSPF_OPAQUE_LINK_LSA; + else if (strncmp(type_name, "opaque-ar", 9) == 0) + type = OSPF_OPAQUE_AREA_LSA; + else if (strncmp(type_name, "opaque-as", 9) == 0) + type = OSPF_OPAQUE_AS_LSA; + else { + if (uj) { + if (use_vrf) + json_object_free(json_vrf); + } + return CMD_WARNING; + } + + if (adv_router && !lsid) + show_lsa_detail_adv_router(vty, ospf, type, adv_router, + json_vrf); + else + show_lsa_detail(vty, ospf, type, lsid, adv_router, json_vrf); + + if (json) { + if (use_vrf) { + if (ospf->vrf_id == VRF_DEFAULT) + json_object_object_add(json, "default", + json_vrf); + else + json_object_object_add(json, ospf->name, + json_vrf); + } + } + + return CMD_SUCCESS; +} + +DEFPY (show_ip_ospf_database, + show_ip_ospf_database_cmd, + "show ip ospf [(1-65535)$instance_id] [vrf $vrf_name] database\ + [<\ + max-age$maxage\ + |self-originate$selforig\ + |<\ + detail$detail\ + |$type_name\ + >\ + [{\ + A.B.C.D$lsid\ + |\ + }]\ + >]\ + [json]", + SHOW_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + VRF_CMD_HELP_STR + "All VRFs\n" + "Database summary\n" + "LSAs in MaxAge list\n" + "Self-originated link states\n" + "Show detailed information\n" + OSPF_LSA_TYPES_DESC + "Link State ID (as an IP address)\n" + "Advertising Router link states\n" + "Advertising Router (as an IP address)\n" + "Self-originated link states\n" + JSON_STR) +{ + struct ospf *ospf; + int ret = CMD_SUCCESS; + bool use_vrf = !!vrf_name; + bool uj = use_json(argc, argv); + struct in_addr *lsid_p = NULL; + struct in_addr *adv_router_p = NULL; + json_object *json = NULL; + + if (uj) + json = json_object_new_object(); + if (lsid_str) + lsid_p = &lsid; + if (adv_router_str) + adv_router_p = &adv_router; + + if (vrf_name && strmatch(vrf_name, "all")) { + struct listnode *node; + bool ospf_output = false; + + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + if (ospf->instance != instance_id) + continue; + + if (adv_router_self) + adv_router_p = &ospf->router_id; + + ospf_output = true; + ret = show_ip_ospf_database_common( + vty, ospf, !!maxage, !!selforig, !!detail, + type_name, lsid_p, adv_router_p, use_vrf, json, + uj); + } + + if (!ospf_output && !uj) + vty_out(vty, "%% OSPF is not enabled\n"); + } else { + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; + ospf = ospf_lookup_by_inst_name(instance_id, vrf_name); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + vty_json(vty, json); + else + vty_out(vty, "%% OSPF instance not found\n"); + return CMD_SUCCESS; + } + if (adv_router_self) + adv_router_p = &ospf->router_id; + + ret = (show_ip_ospf_database_common( + vty, ospf, !!maxage, !!selforig, !!detail, type_name, + lsid_p, adv_router_p, use_vrf, json, uj)); + } + + if (uj) + vty_json(vty, json); + + return ret; +} + +DEFUN (ip_ospf_authentication_args, + ip_ospf_authentication_args_addr_cmd, + "ip ospf authentication [A.B.C.D]", + "IP Information\n" + "OSPF interface commands\n" + "Enable authentication on this interface\n" + "Use null authentication\n" + "Use message-digest authentication\n" + "Use a key-chain for cryptographic authentication keys\n" + "Key-chain name\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_encryption = 3; + int idx_ipv4 = argc-1; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + params = IF_DEF_PARAMS(ifp); + + if (argv[idx_ipv4]->type == IPV4_TKN) { + 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); + } + + /* Handle null authentication */ + if (argv[idx_encryption]->arg[0] == 'n') { + SET_IF_PARAM(params, auth_type); + params->auth_type = OSPF_AUTH_NULL; + return CMD_SUCCESS; + } + + /* Handle message-digest authentication */ + if (argv[idx_encryption]->arg[0] == 'm') { + SET_IF_PARAM(params, auth_type); + params->auth_type = OSPF_AUTH_CRYPTOGRAPHIC; + UNSET_IF_PARAM(params, keychain_name); + XFREE(MTYPE_OSPF_IF_PARAMS, params->keychain_name); + return CMD_SUCCESS; + } + + if (argv[idx_encryption]->arg[0] == 'k') { + SET_IF_PARAM(params, auth_type); + params->auth_type = OSPF_AUTH_CRYPTOGRAPHIC; + SET_IF_PARAM(params, keychain_name); + params->keychain_name = XSTRDUP(MTYPE_OSPF_IF_PARAMS, argv[idx_encryption+1]->arg); + UNSET_IF_PARAM(params, auth_crypt); + return CMD_SUCCESS; + } + + vty_out(vty, "You shouldn't get here!\n"); + return CMD_WARNING_CONFIG_FAILED; +} + +DEFUN (ip_ospf_authentication, + ip_ospf_authentication_addr_cmd, + "ip ospf authentication [A.B.C.D]", + "IP Information\n" + "OSPF interface commands\n" + "Enable authentication on this interface\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_ipv4 = 3; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + + params = IF_DEF_PARAMS(ifp); + + 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); + } + + SET_IF_PARAM(params, auth_type); + params->auth_type = OSPF_AUTH_SIMPLE; + + return CMD_SUCCESS; +} + +DEFUN (no_ip_ospf_authentication_args, + no_ip_ospf_authentication_args_addr_cmd, + "no ip ospf authentication [A.B.C.D]", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Enable authentication on this interface\n" + "Use null authentication\n" + "Use message-digest authentication\n" + "Use a key-chain for cryptographic authentication keys\n" + "Key-chain name\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_encryption = 4; + int idx_ipv4 = argc-1; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + struct route_node *rn; + int auth_type; + + params = IF_DEF_PARAMS(ifp); + + if (argv[idx_ipv4]->type == IPV4_TKN) { + 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) { + vty_out(vty, "Ip Address specified is unknown\n"); + return CMD_WARNING_CONFIG_FAILED; + } + params->auth_type = OSPF_AUTH_NOTSET; + UNSET_IF_PARAM(params, auth_type); + + XFREE(MTYPE_OSPF_IF_PARAMS, params->keychain_name); + UNSET_IF_PARAM(params, keychain_name); + + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, addr); + ospf_if_update_params(ifp, addr); + } + } else { + if (argv[idx_encryption]->arg[0] == 'n') { + auth_type = OSPF_AUTH_NULL; + } else if (argv[idx_encryption]->arg[0] == 'm' || + argv[idx_encryption]->arg[0] == 'k') { + auth_type = OSPF_AUTH_CRYPTOGRAPHIC; + } else { + vty_out(vty, "Unexpected input encountered\n"); + return CMD_WARNING_CONFIG_FAILED; + } + /* + * Here we have a case where the user has entered + * 'no ip ospf authentication (null | message_digest )' + * we need to find if we have any ip addresses underneath it + * that + * correspond to the associated type. + */ + if (params->auth_type == auth_type) { + params->auth_type = OSPF_AUTH_NOTSET; + UNSET_IF_PARAM(params, auth_type); + XFREE(MTYPE_OSPF_IF_PARAMS, params->keychain_name); + UNSET_IF_PARAM(params, keychain_name); + } + + for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; + rn = route_next(rn)) { + if ((params = rn->info)) { + if (params->auth_type == auth_type) { + params->auth_type = OSPF_AUTH_NOTSET; + UNSET_IF_PARAM(params, auth_type); + XFREE(MTYPE_OSPF_IF_PARAMS, params->keychain_name); + UNSET_IF_PARAM(params, keychain_name); + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params( + ifp, rn->p.u.prefix4); + ospf_if_update_params( + ifp, rn->p.u.prefix4); + } + } + } + } + } + + return CMD_SUCCESS; +} + +DEFUN (no_ip_ospf_authentication, + no_ip_ospf_authentication_addr_cmd, + "no ip ospf authentication [A.B.C.D]", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Enable authentication on this interface\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_ipv4 = 4; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + struct route_node *rn; + + params = IF_DEF_PARAMS(ifp); + + 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) { + vty_out(vty, "Ip Address specified is unknown\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + params->auth_type = OSPF_AUTH_NOTSET; + UNSET_IF_PARAM(params, auth_type); + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, addr); + ospf_if_update_params(ifp, addr); + } + } else { + /* + * When a user enters 'no ip ospf authentication' + * We should remove all authentication types from + * the interface. + */ + if ((params->auth_type == OSPF_AUTH_NULL) + || (params->auth_type == OSPF_AUTH_CRYPTOGRAPHIC) + || (params->auth_type == OSPF_AUTH_SIMPLE)) { + params->auth_type = OSPF_AUTH_NOTSET; + UNSET_IF_PARAM(params, auth_type); + } + + for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; + rn = route_next(rn)) { + if ((params = rn->info)) { + + if ((params->auth_type == OSPF_AUTH_NULL) + || (params->auth_type + == OSPF_AUTH_CRYPTOGRAPHIC) + || (params->auth_type + == OSPF_AUTH_SIMPLE)) { + params->auth_type = OSPF_AUTH_NOTSET; + UNSET_IF_PARAM(params, auth_type); + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params( + ifp, rn->p.u.prefix4); + ospf_if_update_params( + ifp, rn->p.u.prefix4); + } + } + } + } + } + + return CMD_SUCCESS; +} + + +DEFUN (ip_ospf_authentication_key, + ip_ospf_authentication_key_addr_cmd, + "ip ospf authentication-key AUTH_KEY [A.B.C.D]", + "IP Information\n" + "OSPF interface commands\n" + "Authentication password (key)\n" + "The OSPF password (key)\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + struct in_addr addr; + struct ospf_if_params *params; + + params = IF_DEF_PARAMS(ifp); + + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &addr)) { + 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); + } + + strlcpy((char *)params->auth_simple, argv[3]->arg, + sizeof(params->auth_simple)); + SET_IF_PARAM(params, auth_simple); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (ospf_authentication_key, + ospf_authentication_key_cmd, + "ospf authentication-key AUTH_KEY [A.B.C.D]", + "OSPF interface commands\n" + VLINK_HELPSTR_AUTH_SIMPLE + "Address of interface\n") +{ + return ip_ospf_authentication_key(self, vty, argc, argv); +} + +DEFUN (no_ip_ospf_authentication_key, + no_ip_ospf_authentication_key_authkey_addr_cmd, + "no ip ospf authentication-key [AUTH_KEY [A.B.C.D]]", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + VLINK_HELPSTR_AUTH_SIMPLE + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + struct in_addr addr; + struct ospf_if_params *params; + params = IF_DEF_PARAMS(ifp); + + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &addr)) { + 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; + } + + memset(params->auth_simple, 0, OSPF_AUTH_SIMPLE_SIZE); + UNSET_IF_PARAM(params, auth_simple); + + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, addr); + ospf_if_update_params(ifp, addr); + } + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (no_ospf_authentication_key, + no_ospf_authentication_key_authkey_addr_cmd, + "no ospf authentication-key [AUTH_KEY [A.B.C.D]]", + NO_STR + "OSPF interface commands\n" + VLINK_HELPSTR_AUTH_SIMPLE + "Address of interface\n") +{ + return no_ip_ospf_authentication_key(self, vty, argc, argv); +} + +DEFUN (ip_ospf_message_digest_key, + ip_ospf_message_digest_key_cmd, + "ip ospf message-digest-key (1-255) md5 KEY [A.B.C.D]", + "IP Information\n" + "OSPF interface commands\n" + "Message digest authentication password (key)\n" + "Key ID\n" + "Use MD5 algorithm\n" + "The OSPF password (key)\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct crypt_key *ck; + uint8_t key_id; + struct in_addr addr; + struct ospf_if_params *params; + + params = IF_DEF_PARAMS(ifp); + int idx = 0; + + argv_find(argv, argc, "(1-255)", &idx); + char *keyid = argv[idx]->arg; + argv_find(argv, argc, "KEY", &idx); + char *cryptkey = argv[idx]->arg; + + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &addr)) { + 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); + } + + key_id = strtol(keyid, NULL, 10); + + /* Remove existing key, if any */ + ospf_crypt_key_delete(params->auth_crypt, key_id); + + ck = ospf_crypt_key_new(); + ck->key_id = (uint8_t)key_id; + strlcpy((char *)ck->auth_key, cryptkey, sizeof(ck->auth_key)); + + ospf_crypt_key_add(params->auth_crypt, ck); + SET_IF_PARAM(params, auth_crypt); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (ospf_message_digest_key, + ospf_message_digest_key_cmd, + "ospf message-digest-key (1-255) md5 KEY [A.B.C.D]", + "OSPF interface commands\n" + "Message digest authentication password (key)\n" + "Key ID\n" + "Use MD5 algorithm\n" + "The OSPF password (key)\n" + "Address of interface\n") +{ + return ip_ospf_message_digest_key(self, vty, argc, argv); +} + +DEFUN (no_ip_ospf_message_digest_key, + no_ip_ospf_message_digest_key_cmd, + "no ip ospf message-digest-key (1-255) [md5 KEY] [A.B.C.D]", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Message digest authentication password (key)\n" + "Key ID\n" + "Use MD5 algorithm\n" + "The OSPF password (key)\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + struct crypt_key *ck; + int key_id; + struct in_addr addr; + struct ospf_if_params *params; + params = IF_DEF_PARAMS(ifp); + + argv_find(argv, argc, "(1-255)", &idx); + char *keyid = argv[idx]->arg; + + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &addr)) { + 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; + } + + key_id = strtol(keyid, NULL, 10); + ck = ospf_crypt_key_lookup(params->auth_crypt, key_id); + if (ck == NULL) { + vty_out(vty, "OSPF: Key %d does not exist\n", key_id); + return CMD_WARNING_CONFIG_FAILED; + } + + ospf_crypt_key_delete(params->auth_crypt, key_id); + + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, addr); + ospf_if_update_params(ifp, addr); + } + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (no_ospf_message_digest_key, + no_ospf_message_digest_key_cmd, + "no ospf message-digest-key (1-255) [md5 KEY] [A.B.C.D]", + NO_STR + "OSPF interface commands\n" + "Message digest authentication password (key)\n" + "Key ID\n" + "Use MD5 algorithm\n" + "The OSPF password (key)\n" + "Address of interface\n") +{ + return no_ip_ospf_message_digest_key(self, vty, argc, argv); +} + +DEFUN (ip_ospf_cost, + ip_ospf_cost_cmd, + "ip ospf cost (1-65535) [A.B.C.D]", + "IP Information\n" + "OSPF interface commands\n" + "Interface cost\n" + "Cost\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + uint32_t cost = OSPF_OUTPUT_COST_DEFAULT; + struct in_addr addr; + struct ospf_if_params *params; + params = IF_DEF_PARAMS(ifp); + + // get arguments + char *coststr = NULL, *ifaddr = NULL; + + argv_find(argv, argc, "(1-65535)", &idx); + coststr = argv[idx]->arg; + cost = strtol(coststr, NULL, 10); + + ifaddr = argv_find(argv, argc, "A.B.C.D", &idx) ? argv[idx]->arg : NULL; + if (ifaddr) { + if (!inet_aton(ifaddr, &addr)) { + 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); + } + + SET_IF_PARAM(params, output_cost_cmd); + params->output_cost_cmd = cost; + + ospf_if_recalculate_output_cost(ifp); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (ospf_cost, + ospf_cost_cmd, + "ospf cost (1-65535) [A.B.C.D]", + "OSPF interface commands\n" + "Interface cost\n" + "Cost\n" + "Address of interface\n") +{ + return ip_ospf_cost(self, vty, argc, argv); +} + +DEFUN (no_ip_ospf_cost, + no_ip_ospf_cost_cmd, + "no ip ospf cost [(1-65535)] [A.B.C.D]", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Interface cost\n" + "Cost\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + struct in_addr addr; + struct ospf_if_params *params; + + params = IF_DEF_PARAMS(ifp); + + // get arguments + char *ifaddr = NULL; + ifaddr = argv_find(argv, argc, "A.B.C.D", &idx) ? argv[idx]->arg : NULL; + + /* According to the semantics we are mimicking "no ip ospf cost N" is + * always treated as "no ip ospf cost" regardless of the actual value + * of N already configured for the interface. Thus ignore cost. */ + + if (ifaddr) { + if (!inet_aton(ifaddr, &addr)) { + 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; + } + + UNSET_IF_PARAM(params, output_cost_cmd); + + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, addr); + ospf_if_update_params(ifp, addr); + } + + ospf_if_recalculate_output_cost(ifp); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (no_ospf_cost, + no_ospf_cost_cmd, + "no ospf cost [(1-65535)] [A.B.C.D]", + NO_STR + "OSPF interface commands\n" + "Interface cost\n" + "Cost\n" + "Address of interface\n") +{ + return no_ip_ospf_cost(self, vty, argc, argv); +} + +static void ospf_nbr_timer_update(struct ospf_interface *oi) +{ + struct route_node *rn; + struct ospf_neighbor *nbr; + + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + nbr = rn->info; + + if (!nbr) + continue; + + nbr->v_inactivity = OSPF_IF_PARAM(oi, v_wait); + nbr->v_db_desc = OSPF_IF_PARAM(oi, retransmit_interval); + nbr->v_ls_req = OSPF_IF_PARAM(oi, retransmit_interval); + nbr->v_ls_upd = OSPF_IF_PARAM(oi, retransmit_interval); + } +} + +static int ospf_vty_dead_interval_set(struct vty *vty, const char *interval_str, + const char *nbr_str, + const char *fast_hello_str) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + uint32_t seconds; + uint8_t hellomult; + struct in_addr addr; + int ret; + struct ospf_if_params *params; + struct ospf_interface *oi; + struct route_node *rn; + + params = IF_DEF_PARAMS(ifp); + + if (nbr_str) { + ret = inet_aton(nbr_str, &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); + } + + if (interval_str) { + seconds = strtoul(interval_str, NULL, 10); + + /* reset fast_hello too, just to be sure */ + UNSET_IF_PARAM(params, fast_hello); + params->fast_hello = OSPF_FAST_HELLO_DEFAULT; + } else if (fast_hello_str) { + hellomult = strtoul(fast_hello_str, NULL, 10); + /* 1s dead-interval with sub-second hellos desired */ + seconds = OSPF_ROUTER_DEAD_INTERVAL_MINIMAL; + SET_IF_PARAM(params, fast_hello); + params->fast_hello = hellomult; + } else { + vty_out(vty, + "Please specify dead-interval or hello-multiplier\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + SET_IF_PARAM(params, v_wait); + params->v_wait = seconds; + params->is_v_wait_set = true; + + /* Update timer values in neighbor structure. */ + if (nbr_str) { + struct ospf *ospf = NULL; + + ospf = ifp->vrf->info; + if (ospf) { + oi = ospf_if_lookup_by_local_addr(ospf, ifp, addr); + if (oi) + ospf_nbr_timer_update(oi); + } + } else { + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) + if ((oi = rn->info)) + ospf_nbr_timer_update(oi); + } + + return CMD_SUCCESS; +} + +DEFUN (ip_ospf_dead_interval, + ip_ospf_dead_interval_cmd, + "ip ospf dead-interval (1-65535) [A.B.C.D]", + "IP Information\n" + "OSPF interface commands\n" + "Interval time after which a neighbor is declared down\n" + "Seconds\n" + "Address of interface\n") +{ + int idx = 0; + char *interval = argv_find(argv, argc, "(1-65535)", &idx) + ? argv[idx]->arg + : NULL; + char *ifaddr = + argv_find(argv, argc, "A.B.C.D", &idx) ? argv[idx]->arg : NULL; + return ospf_vty_dead_interval_set(vty, interval, ifaddr, NULL); +} + + +DEFUN_HIDDEN (ospf_dead_interval, + ospf_dead_interval_cmd, + "ospf dead-interval (1-65535) [A.B.C.D]", + "OSPF interface commands\n" + "Interval time after which a neighbor is declared down\n" + "Seconds\n" + "Address of interface\n") +{ + return ip_ospf_dead_interval(self, vty, argc, argv); +} + +DEFUN (ip_ospf_dead_interval_minimal, + ip_ospf_dead_interval_minimal_addr_cmd, + "ip ospf dead-interval minimal hello-multiplier (1-10) [A.B.C.D]", + "IP Information\n" + "OSPF interface commands\n" + "Interval time after which a neighbor is declared down\n" + "Minimal 1s dead-interval with fast sub-second hellos\n" + "Hello multiplier factor\n" + "Number of Hellos to send each second\n" + "Address of interface\n") +{ + int idx_number = 5; + int idx_ipv4 = 6; + if (argc == 7) + return ospf_vty_dead_interval_set( + vty, NULL, argv[idx_ipv4]->arg, argv[idx_number]->arg); + else + return ospf_vty_dead_interval_set(vty, NULL, NULL, + argv[idx_number]->arg); +} + +DEFUN (no_ip_ospf_dead_interval, + no_ip_ospf_dead_interval_cmd, + "no ip ospf dead-interval [<(1-65535)|minimal hello-multiplier (1-10)> [A.B.C.D]]", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Interval time after which a neighbor is declared down\n" + "Seconds\n" + "Minimal 1s dead-interval with fast sub-second hellos\n" + "Hello multiplier factor\n" + "Number of Hellos to send each second\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_ipv4 = argc - 1; + struct in_addr addr = {.s_addr = 0L}; + int ret; + struct ospf_if_params *params; + struct ospf_interface *oi; + struct route_node *rn; + + params = IF_DEF_PARAMS(ifp); + + if (argv[idx_ipv4]->type == IPV4_TKN) { + 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; + } + + UNSET_IF_PARAM(params, v_wait); + params->v_wait = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; + params->is_v_wait_set = false; + + UNSET_IF_PARAM(params, fast_hello); + params->fast_hello = OSPF_FAST_HELLO_DEFAULT; + + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, addr); + ospf_if_update_params(ifp, addr); + } + + /* Update timer values in neighbor structure. */ + if (argc == 1) { + struct ospf *ospf = NULL; + + ospf = ifp->vrf->info; + if (ospf) { + oi = ospf_if_lookup_by_local_addr(ospf, ifp, addr); + if (oi) + ospf_nbr_timer_update(oi); + } + } else { + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) + if ((oi = rn->info)) + ospf_nbr_timer_update(oi); + } + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (no_ospf_dead_interval, + no_ospf_dead_interval_cmd, + "no ospf dead-interval [<(1-65535)|minimal hello-multiplier (1-10)> [A.B.C.D]]", + NO_STR + "OSPF interface commands\n" + "Interval time after which a neighbor is declared down\n" + "Seconds\n" + "Minimal 1s dead-interval with fast sub-second hellos\n" + "Hello multiplier factor\n" + "Number of Hellos to send each second\n" + "Address of interface\n") +{ + return no_ip_ospf_dead_interval(self, vty, argc, argv); +} + +DEFUN (ip_ospf_hello_interval, + ip_ospf_hello_interval_cmd, + "ip ospf hello-interval (1-65535) [A.B.C.D]", + "IP Information\n" + "OSPF interface commands\n" + "Time between HELLO packets\n" + "Seconds\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + struct in_addr addr = {.s_addr = 0L}; + struct ospf_if_params *params; + params = IF_DEF_PARAMS(ifp); + uint32_t seconds = 0; + bool is_addr = false; + uint32_t old_interval = 0; + + argv_find(argv, argc, "(1-65535)", &idx); + seconds = strtol(argv[idx]->arg, NULL, 10); + + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &addr)) { + 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); + is_addr = true; + } + + old_interval = params->v_hello; + + /* Return, if same interval is configured. */ + if (old_interval == seconds) + return CMD_SUCCESS; + + SET_IF_PARAM(params, v_hello); + params->v_hello = seconds; + + if (!params->is_v_wait_set) { + SET_IF_PARAM(params, v_wait); + /* As per RFC 4062 + * The router dead interval should + * be some multiple of the HelloInterval (perhaps 4 times the + * hello interval) and must be the same for all routers + * attached to a common network. + */ + params->v_wait = 4 * seconds; + } + + ospf_reset_hello_timer(ifp, addr, is_addr); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (ospf_hello_interval, + ospf_hello_interval_cmd, + "ospf hello-interval (1-65535) [A.B.C.D]", + "OSPF interface commands\n" + "Time between HELLO packets\n" + "Seconds\n" + "Address of interface\n") +{ + return ip_ospf_hello_interval(self, vty, argc, argv); +} + +DEFUN (no_ip_ospf_hello_interval, + no_ip_ospf_hello_interval_cmd, + "no ip ospf hello-interval [(1-65535) [A.B.C.D]]", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Time between HELLO packets\n" // ignored + "Seconds\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + struct in_addr addr = {.s_addr = 0L}; + struct ospf_if_params *params; + struct route_node *rn; + + params = IF_DEF_PARAMS(ifp); + + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &addr)) { + 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; + } + + UNSET_IF_PARAM(params, v_hello); + params->v_hello = OSPF_HELLO_INTERVAL_DEFAULT; + + if (!params->is_v_wait_set) { + UNSET_IF_PARAM(params, v_wait); + params->v_wait = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; + } + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi = rn->info; + + if (!oi) + continue; + + oi->type = IF_DEF_PARAMS(ifp)->type; + oi->ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn; + + if (oi->state > ISM_Down) { + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown); + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp); + } + } + + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, addr); + ospf_if_update_params(ifp, addr); + } + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (no_ospf_hello_interval, + no_ospf_hello_interval_cmd, + "no ospf hello-interval [(1-65535) [A.B.C.D]]", + NO_STR + "OSPF interface commands\n" + "Time between HELLO packets\n" // ignored + "Seconds\n" + "Address of interface\n") +{ + return no_ip_ospf_hello_interval(self, vty, argc, argv); +} + +DEFUN(ip_ospf_network, ip_ospf_network_cmd, + "ip ospf network ", + "IP Information\n" + "OSPF interface commands\n" + "Network type\n" + "Specify OSPF broadcast multi-access network\n" + "Specify OSPF NBMA network\n" + "Specify OSPF point-to-multipoint network\n" + "Specify OSPF delayed reflooding of LSAs received on P2MP interface\n" + "Specify OSPF point-to-point network\n" + "Specify OSPF point-to-point DMVPN network\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + int old_type = IF_DEF_PARAMS(ifp)->type; + uint8_t old_ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn; + uint8_t old_p2mp_delay_reflood = IF_DEF_PARAMS(ifp)->p2mp_delay_reflood; + struct route_node *rn; + + if (old_type == OSPF_IFTYPE_LOOPBACK) { + vty_out(vty, + "This is a loopback interface. Can't set network type.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0; + IF_DEF_PARAMS(ifp)->p2mp_delay_reflood = + OSPF_P2MP_DELAY_REFLOOD_DEFAULT; + + if (argv_find(argv, argc, "broadcast", &idx)) + IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_BROADCAST; + else if (argv_find(argv, argc, "non-broadcast", &idx)) + IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_NBMA; + else if (argv_find(argv, argc, "point-to-multipoint", &idx)) { + IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOMULTIPOINT; + if (argv_find(argv, argc, "delay-reflood", &idx)) + IF_DEF_PARAMS(ifp)->p2mp_delay_reflood = true; + } else if (argv_find(argv, argc, "point-to-point", &idx)) { + IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOPOINT; + if (argv_find(argv, argc, "dmvpn", &idx)) + IF_DEF_PARAMS(ifp)->ptp_dmvpn = 1; + } + + if (IF_DEF_PARAMS(ifp)->type == old_type && + IF_DEF_PARAMS(ifp)->ptp_dmvpn == old_ptp_dmvpn && + IF_DEF_PARAMS(ifp)->p2mp_delay_reflood == old_p2mp_delay_reflood) + return CMD_SUCCESS; + + SET_IF_PARAM(IF_DEF_PARAMS(ifp), type); + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi = rn->info; + + if (!oi) + continue; + + oi->type = IF_DEF_PARAMS(ifp)->type; + oi->ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn; + oi->p2mp_delay_reflood = IF_DEF_PARAMS(ifp)->p2mp_delay_reflood; + + /* + * The OSPF interface only needs to be flapped if the network + * type or DMVPN parameter changes. + */ + if (IF_DEF_PARAMS(ifp)->type != old_type || + IF_DEF_PARAMS(ifp)->ptp_dmvpn != old_ptp_dmvpn) { + if (oi->state > ISM_Down) { + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown); + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp); + } + } + } + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (ospf_network, + ospf_network_cmd, + "ospf network ", + "OSPF interface commands\n" + "Network type\n" + "Specify OSPF broadcast multi-access network\n" + "Specify OSPF NBMA network\n" + "Specify OSPF point-to-multipoint network\n" + "Specify OSPF point-to-point network\n") +{ + return ip_ospf_network(self, vty, argc, argv); +} + +DEFUN (no_ip_ospf_network, + no_ip_ospf_network_cmd, + "no ip ospf network []", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Network type\n" + "Specify OSPF broadcast multi-access network\n" + "Specify OSPF NBMA network\n" + "Specify OSPF point-to-multipoint network\n" + "Specify OSPF point-to-point network\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int old_type = IF_DEF_PARAMS(ifp)->type; + struct route_node *rn; + + IF_DEF_PARAMS(ifp)->type = ospf_default_iftype(ifp); + IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0; + IF_DEF_PARAMS(ifp)->p2mp_delay_reflood = + OSPF_P2MP_DELAY_REFLOOD_DEFAULT; + + if (IF_DEF_PARAMS(ifp)->type == old_type) + return CMD_SUCCESS; + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi = rn->info; + + if (!oi) + continue; + + oi->type = IF_DEF_PARAMS(ifp)->type; + oi->ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn; + oi->p2mp_delay_reflood = IF_DEF_PARAMS(ifp)->p2mp_delay_reflood; + + if (oi->state > ISM_Down) { + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown); + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp); + } + } + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (no_ospf_network, + no_ospf_network_cmd, + "no ospf network []", + NO_STR + "OSPF interface commands\n" + "Network type\n" + "Specify OSPF broadcast multi-access network\n" + "Specify OSPF NBMA network\n" + "Specify OSPF point-to-multipoint network\n" + "Specify OSPF point-to-point network\n") +{ + return no_ip_ospf_network(self, vty, argc, argv); +} + +DEFUN (ip_ospf_priority, + ip_ospf_priority_cmd, + "ip ospf priority (0-255) [A.B.C.D]", + "IP Information\n" + "OSPF interface commands\n" + "Router priority\n" + "Priority\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + long priority; + struct route_node *rn; + struct in_addr addr; + struct ospf_if_params *params; + params = IF_DEF_PARAMS(ifp); + + argv_find(argv, argc, "(0-255)", &idx); + priority = strtol(argv[idx]->arg, NULL, 10); + + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &addr)) { + 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); + } + + SET_IF_PARAM(params, priority); + params->priority = priority; + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi = rn->info; + + if (!oi) + continue; + + if (PRIORITY(oi) != OSPF_IF_PARAM(oi, priority)) { + PRIORITY(oi) = OSPF_IF_PARAM(oi, priority); + OSPF_ISM_EVENT_SCHEDULE(oi, ISM_NeighborChange); + } + } + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (ospf_priority, + ospf_priority_cmd, + "ospf priority (0-255) [A.B.C.D]", + "OSPF interface commands\n" + "Router priority\n" + "Priority\n" + "Address of interface\n") +{ + return ip_ospf_priority(self, vty, argc, argv); +} + +DEFUN (no_ip_ospf_priority, + no_ip_ospf_priority_cmd, + "no ip ospf priority [(0-255) [A.B.C.D]]", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Router priority\n" // ignored + "Priority\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + struct route_node *rn; + struct in_addr addr; + struct ospf_if_params *params; + + params = IF_DEF_PARAMS(ifp); + + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &addr)) { + 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; + } + + UNSET_IF_PARAM(params, priority); + params->priority = OSPF_ROUTER_PRIORITY_DEFAULT; + + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, addr); + ospf_if_update_params(ifp, addr); + } + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi = rn->info; + + if (!oi) + continue; + + if (PRIORITY(oi) != OSPF_IF_PARAM(oi, priority)) { + PRIORITY(oi) = OSPF_IF_PARAM(oi, priority); + OSPF_ISM_EVENT_SCHEDULE(oi, ISM_NeighborChange); + } + } + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (no_ospf_priority, + no_ospf_priority_cmd, + "no ospf priority [(0-255) [A.B.C.D]]", + NO_STR + "OSPF interface commands\n" + "Router priority\n" + "Priority\n" + "Address of interface\n") +{ + return no_ip_ospf_priority(self, vty, argc, argv); +} + +DEFUN (ip_ospf_retransmit_interval, + ip_ospf_retransmit_interval_addr_cmd, + "ip ospf retransmit-interval (1-65535) [A.B.C.D]", + "IP Information\n" + "OSPF interface commands\n" + "Time between retransmitting lost link state advertisements\n" + "Seconds\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + uint32_t seconds; + struct in_addr addr; + struct ospf_if_params *params; + params = IF_DEF_PARAMS(ifp); + + argv_find(argv, argc, "(1-65535)", &idx); + seconds = strtol(argv[idx]->arg, NULL, 10); + + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &addr)) { + 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); + } + + SET_IF_PARAM(params, retransmit_interval); + params->retransmit_interval = seconds; + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (ospf_retransmit_interval, + ospf_retransmit_interval_cmd, + "ospf retransmit-interval (1-65535) [A.B.C.D]", + "OSPF interface commands\n" + "Time between retransmitting lost link state advertisements\n" + "Seconds\n" + "Address of interface\n") +{ + return ip_ospf_retransmit_interval(self, vty, argc, argv); +} + +DEFUN (no_ip_ospf_retransmit_interval, + no_ip_ospf_retransmit_interval_addr_cmd, + "no ip ospf retransmit-interval [(1-65535)] [A.B.C.D]", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Time between retransmitting lost link state advertisements\n" + "Seconds\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + struct in_addr addr; + struct ospf_if_params *params; + + params = IF_DEF_PARAMS(ifp); + + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &addr)) { + 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; + } + + UNSET_IF_PARAM(params, retransmit_interval); + params->retransmit_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT; + + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, addr); + ospf_if_update_params(ifp, addr); + } + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (no_ospf_retransmit_interval, + no_ospf_retransmit_interval_cmd, + "no ospf retransmit-interval [(1-65535)] [A.B.C.D]", + NO_STR + "OSPF interface commands\n" + "Time between retransmitting lost link state advertisements\n" + "Seconds\n" + "Address of interface\n") +{ + return no_ip_ospf_retransmit_interval(self, vty, argc, argv); +} + +DEFPY (ip_ospf_gr_hdelay, + ip_ospf_gr_hdelay_cmd, + "ip ospf graceful-restart hello-delay (1-1800)", + IP_STR + "OSPF interface commands\n" + "Graceful Restart parameters\n" + "Delay the sending of the first hello packets.\n" + "Delay in seconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + + params = IF_DEF_PARAMS(ifp); + + /* Note: new or updated value won't affect ongoing graceful restart. */ + SET_IF_PARAM(params, v_gr_hello_delay); + params->v_gr_hello_delay = hello_delay; + + return CMD_SUCCESS; +} + +DEFPY (no_ip_ospf_gr_hdelay, + no_ip_ospf_gr_hdelay_cmd, + "no ip ospf graceful-restart hello-delay [(1-1800)]", + NO_STR + IP_STR + "OSPF interface commands\n" + "Graceful Restart parameters\n" + "Delay the sending of the first hello packets.\n" + "Delay in seconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + struct route_node *rn; + + params = IF_DEF_PARAMS(ifp); + UNSET_IF_PARAM(params, v_gr_hello_delay); + params->v_gr_hello_delay = OSPF_HELLO_DELAY_DEFAULT; + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi; + + oi = rn->info; + if (!oi) + continue; + + oi->gr.hello_delay.elapsed_seconds = 0; + EVENT_OFF(oi->gr.hello_delay.t_grace_send); + } + + return CMD_SUCCESS; +} + +DEFUN (ip_ospf_transmit_delay, + ip_ospf_transmit_delay_addr_cmd, + "ip ospf transmit-delay (1-65535) [A.B.C.D]", + "IP Information\n" + "OSPF interface commands\n" + "Link state transmit delay\n" + "Seconds\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + uint32_t seconds; + struct in_addr addr; + struct ospf_if_params *params; + + params = IF_DEF_PARAMS(ifp); + argv_find(argv, argc, "(1-65535)", &idx); + seconds = strtol(argv[idx]->arg, NULL, 10); + + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &addr)) { + 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); + } + + SET_IF_PARAM(params, transmit_delay); + params->transmit_delay = seconds; + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (ospf_transmit_delay, + ospf_transmit_delay_cmd, + "ospf transmit-delay (1-65535) [A.B.C.D]", + "OSPF interface commands\n" + "Link state transmit delay\n" + "Seconds\n" + "Address of interface\n") +{ + return ip_ospf_transmit_delay(self, vty, argc, argv); +} + +DEFUN (no_ip_ospf_transmit_delay, + no_ip_ospf_transmit_delay_addr_cmd, + "no ip ospf transmit-delay [(1-65535)] [A.B.C.D]", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Link state transmit delay\n" + "Seconds\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + struct in_addr addr; + struct ospf_if_params *params; + + params = IF_DEF_PARAMS(ifp); + + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &addr)) { + 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; + } + + UNSET_IF_PARAM(params, transmit_delay); + params->transmit_delay = OSPF_TRANSMIT_DELAY_DEFAULT; + + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, addr); + ospf_if_update_params(ifp, addr); + } + + return CMD_SUCCESS; +} + + +DEFUN_HIDDEN (no_ospf_transmit_delay, + no_ospf_transmit_delay_cmd, + "no ospf transmit-delay [(1-65535) [A.B.C.D]]", + NO_STR + "OSPF interface commands\n" + "Link state transmit delay\n" + "Seconds\n" + "Address of interface\n") +{ + return no_ip_ospf_transmit_delay(self, vty, argc, argv); +} + +DEFUN (ip_ospf_area, + ip_ospf_area_cmd, + "ip ospf [(1-65535)] area [A.B.C.D]", + "IP Information\n" + "OSPF interface commands\n" + "Instance ID\n" + "Enable OSPF on this interface\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + int format, ret; + struct in_addr area_id; + struct in_addr addr; + struct ospf_if_params *params = NULL; + struct route_node *rn; + struct ospf *ospf = NULL; + unsigned short instance = 0; + char *areaid; + uint32_t count = 0; + + if (argv_find(argv, argc, "(1-65535)", &idx)) + instance = strtol(argv[idx]->arg, NULL, 10); + + argv_find(argv, argc, "area", &idx); + areaid = argv[idx + 1]->arg; + + if (!instance) + ospf = ifp->vrf->info; + else + ospf = ospf_lookup_instance(instance); + + if (instance && instance != ospf_instance) { + /* + * At this point we know we have received + * an instance and there is no ospf instance + * associated with it. This means we are + * in a situation where we have an + * ospf command that is setup for a different + * process(instance). We need to safely + * remove the command from ourselves and + * allow the other instance(process) handle + * the configuration command. + */ + count = 0; + + params = IF_DEF_PARAMS(ifp); + if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) { + UNSET_IF_PARAM(params, if_area); + count++; + } + + for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn)) + if ((params = rn->info) && OSPF_IF_PARAM_CONFIGURED(params, if_area)) { + UNSET_IF_PARAM(params, if_area); + count++; + } + + if (count > 0) { + ospf = ifp->vrf->info; + if (ospf) + ospf_interface_area_unset(ospf, ifp); + } + + return CMD_NOT_MY_INSTANCE; + } + + ret = str2area_id(areaid, &area_id, &format); + if (ret < 0) { + vty_out(vty, "Please specify area by A.B.C.D|<0-4294967295>\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (memcmp(ifp->name, "VLINK", 5) == 0) { + vty_out(vty, "Cannot enable OSPF on a virtual link.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (ospf) { + for (rn = route_top(ospf->networks); rn; rn = route_next(rn)) { + if (rn->info != NULL) { + vty_out(vty, + "Please remove all network commands first.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + } + + params = IF_DEF_PARAMS(ifp); + if (OSPF_IF_PARAM_CONFIGURED(params, if_area) + && !IPV4_ADDR_SAME(¶ms->if_area, &area_id)) { + vty_out(vty, + "Must remove previous area config before changing ospf area \n"); + return CMD_WARNING_CONFIG_FAILED; + } + + // Check if we have an address arg and proccess it + if (argc == idx + 3) { + if (!inet_aton(argv[idx + 2]->arg, &addr)) { + vty_out(vty, + "Please specify Intf Address by A.B.C.D\n"); + return CMD_WARNING_CONFIG_FAILED; + } + // update/create address-level params + params = ospf_get_if_params((ifp), (addr)); + if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) { + if (!IPV4_ADDR_SAME(¶ms->if_area, &area_id)) { + vty_out(vty, + "Must remove previous area/address config before changing ospf area\n"); + return CMD_WARNING_CONFIG_FAILED; + } else + return CMD_SUCCESS; + } + ospf_if_update_params((ifp), (addr)); + } + + /* enable ospf on this interface with area_id */ + if (params) { + SET_IF_PARAM(params, if_area); + params->if_area = area_id; + params->if_area_id_fmt = format; + } + + if (ospf) + ospf_interface_area_set(ospf, ifp); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_ospf_area, + no_ip_ospf_area_cmd, + "no ip ospf [(1-65535)] area [ [A.B.C.D]]", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Instance ID\n" + "Disable OSPF on this interface\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx = 0; + struct ospf *ospf; + struct ospf_if_params *params; + unsigned short instance = 0; + struct in_addr addr; + struct in_addr area_id; + + if (argv_find(argv, argc, "(1-65535)", &idx)) + instance = strtol(argv[idx]->arg, NULL, 10); + + if (!instance) + ospf = ifp->vrf->info; + else + ospf = ospf_lookup_instance(instance); + + if (instance && instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + argv_find(argv, argc, "area", &idx); + + // Check if we have an address arg and proccess it + if (argc == idx + 3) { + if (!inet_aton(argv[idx + 2]->arg, &addr)) { + vty_out(vty, + "Please specify Intf 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); + + area_id = params->if_area; + if (!OSPF_IF_PARAM_CONFIGURED(params, if_area)) { + vty_out(vty, + "Can't find specified interface area configuration.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + UNSET_IF_PARAM(params, if_area); + if (params != IF_DEF_PARAMS((ifp))) { + ospf_free_if_params((ifp), (addr)); + ospf_if_update_params((ifp), (addr)); + } + + if (ospf) { + ospf_interface_area_unset(ospf, ifp); + ospf_area_check_free(ospf, area_id); + } + + 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 = {.s_addr = INADDR_ANY}; + 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); + } + + ospf_passive_interface_update(ifp, params, addr, OSPF_IF_PASSIVE); + + 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 = {.s_addr = INADDR_ANY}; + 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); + } + + ospf_passive_interface_update(ifp, params, addr, OSPF_IF_ACTIVE); + + 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 RMAP_NAME}]", + REDIST_STR + FRR_REDIST_HELP_STR_OSPFD + "Metric for redistributed routes\n" + "OSPF default metric\n" + "OSPF exterior metric type for redistributed routes\n" + "Set OSPF External Type 1/2 metrics\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_protocol = 1; + int source; + int type = -1; + int metric = -1; + struct ospf_redist *red; + int idx = 0; + bool update = false; + + /* Get distribute source. */ + source = proto_redistnum(AFI_IP, argv[idx_protocol]->text); + if (source < 0) + return CMD_WARNING_CONFIG_FAILED; + + /* Get metric value. */ + if (argv_find(argv, argc, "(0-16777214)", &idx)) { + if (!str2metric(argv[idx]->arg, &metric)) + return CMD_WARNING_CONFIG_FAILED; + } + idx = 1; + /* Get metric type. */ + if (argv_find(argv, argc, "(1-2)", &idx)) { + if (!str2metric_type(argv[idx]->arg, &type)) + return CMD_WARNING_CONFIG_FAILED; + } + idx = 1; + + red = ospf_redist_lookup(ospf, source, 0); + if (!red) + red = ospf_redist_add(ospf, source, 0); + else + update = true; + + /* Get route-map */ + if (argv_find(argv, argc, "route-map", &idx)) { + ospf_routemap_set(red, argv[idx + 1]->arg); + } else + ospf_routemap_unset(red); + + if (update) + return ospf_redistribute_update(ospf, red, source, 0, type, + metric); + else + return ospf_redistribute_set(ospf, red, source, 0, type, + metric); +} + +DEFUN (no_ospf_redistribute_source, + no_ospf_redistribute_source_cmd, + "no redistribute " FRR_REDIST_STR_OSPFD " [{metric (0-16777214)|metric-type (1-2)|route-map RMAP_NAME}]", + NO_STR + REDIST_STR + FRR_REDIST_HELP_STR_OSPFD + "Metric for redistributed routes\n" + "OSPF default metric\n" + "OSPF exterior metric type for redistributed routes\n" + "Set OSPF External Type 1/2 metrics\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_protocol = 2; + int source; + struct ospf_redist *red; + + source = proto_redistnum(AFI_IP, argv[idx_protocol]->text); + if (source < 0) + return CMD_WARNING_CONFIG_FAILED; + + red = ospf_redist_lookup(ospf, source, 0); + if (!red) + return CMD_SUCCESS; + + ospf_routemap_unset(red); + ospf_redist_del(ospf, source, 0); + + return ospf_redistribute_unset(ospf, source, 0); +} + +DEFUN (ospf_redistribute_instance_source, + ospf_redistribute_instance_source_cmd, + "redistribute (1-65535) [{metric (0-16777214)|metric-type (1-2)|route-map RMAP_NAME}]", + REDIST_STR + "Open Shortest Path First\n" + "Non-main Kernel Routing Table\n" + "Instance ID/Table ID\n" + "Metric for redistributed routes\n" + "OSPF default metric\n" + "OSPF exterior metric type for redistributed routes\n" + "Set OSPF External Type 1/2 metrics\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ospf_table = 1; + int idx_number = 2; + int idx = 3; + int source; + int type = -1; + int metric = -1; + unsigned short instance; + struct ospf_redist *red; + bool update = false; + + source = proto_redistnum(AFI_IP, argv[idx_ospf_table]->text); + + if (source < 0) { + vty_out(vty, "Unknown instance redistribution\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + + if ((source == ZEBRA_ROUTE_OSPF) && !ospf->instance) { + vty_out(vty, + "Instance redistribution in non-instanced OSPF not allowed\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if ((source == ZEBRA_ROUTE_OSPF) && (ospf->instance == instance)) { + vty_out(vty, "Same instance OSPF redistribution not allowed\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Get metric value. */ + if (argv_find(argv, argc, "metric", &idx)) + if (!str2metric(argv[idx + 1]->arg, &metric)) + return CMD_WARNING_CONFIG_FAILED; + + idx = 3; + /* Get metric type. */ + if (argv_find(argv, argc, "metric-type", &idx)) + if (!str2metric_type(argv[idx + 1]->arg, &type)) + return CMD_WARNING_CONFIG_FAILED; + + red = ospf_redist_lookup(ospf, source, instance); + if (!red) + red = ospf_redist_add(ospf, source, instance); + else + update = true; + + idx = 3; + if (argv_find(argv, argc, "route-map", &idx)) + ospf_routemap_set(red, argv[idx + 1]->arg); + else + ospf_routemap_unset(red); + + if (update) + return ospf_redistribute_update(ospf, red, source, instance, + type, metric); + else + return ospf_redistribute_set(ospf, red, source, instance, type, + metric); +} + +DEFUN (no_ospf_redistribute_instance_source, + no_ospf_redistribute_instance_source_cmd, + "no redistribute (1-65535) [{metric (0-16777214)|metric-type (1-2)|route-map RMAP_NAME}]", + NO_STR + REDIST_STR + "Open Shortest Path First\n" + "Non-main Kernel Routing Table\n" + "Instance ID/Table Id\n" + "Metric for redistributed routes\n" + "OSPF default metric\n" + "OSPF exterior metric type for redistributed routes\n" + "Set OSPF External Type 1/2 metrics\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_ospf_table = 2; + int idx_number = 3; + unsigned int instance; + struct ospf_redist *red; + int source; + + if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0) + source = ZEBRA_ROUTE_OSPF; + else + source = ZEBRA_ROUTE_TABLE; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + + if ((source == ZEBRA_ROUTE_OSPF) && !ospf->instance) { + vty_out(vty, + "Instance redistribution in non-instanced OSPF not allowed\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if ((source == ZEBRA_ROUTE_OSPF) && (ospf->instance == instance)) { + vty_out(vty, "Same instance OSPF redistribution not allowed\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + red = ospf_redist_lookup(ospf, source, instance); + if (!red) + return CMD_SUCCESS; + + ospf_routemap_unset(red); + ospf_redist_del(ospf, source, instance); + + return ospf_redistribute_unset(ospf, source, instance); +} + +DEFUN (ospf_distribute_list_out, + ospf_distribute_list_out_cmd, + "distribute-list ACCESSLIST4_NAME out " FRR_REDIST_STR_OSPFD, + "Filter networks in routing updates\n" + "Access-list name\n" + OUT_STR + FRR_REDIST_HELP_STR_OSPFD) +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_word = 1; + int source; + + char *proto = argv[argc - 1]->text; + + /* Get distribute source. */ + source = proto_redistnum(AFI_IP, proto); + if (source < 0) + return CMD_WARNING_CONFIG_FAILED; + + return ospf_distribute_list_out_set(ospf, source, argv[idx_word]->arg); +} + +DEFUN (no_ospf_distribute_list_out, + no_ospf_distribute_list_out_cmd, + "no distribute-list ACCESSLIST4_NAME out " FRR_REDIST_STR_OSPFD, + NO_STR + "Filter networks in routing updates\n" + "Access-list name\n" + OUT_STR + FRR_REDIST_HELP_STR_OSPFD) +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_word = 2; + int source; + + char *proto = argv[argc - 1]->text; + source = proto_redistnum(AFI_IP, proto); + if (source < 0) + return CMD_WARNING_CONFIG_FAILED; + + return ospf_distribute_list_out_unset(ospf, source, + argv[idx_word]->arg); +} + +/* Default information originate. */ +DEFUN (ospf_default_information_originate, + ospf_default_information_originate_cmd, + "default-information originate [{always|metric (0-16777214)|metric-type (1-2)|route-map RMAP_NAME}]", + "Control distribution of default information\n" + "Distribute a default route\n" + "Always advertise default route\n" + "OSPF default metric\n" + "OSPF metric\n" + "OSPF metric type for default routes\n" + "Set OSPF External Type 1/2 metrics\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int default_originate = DEFAULT_ORIGINATE_ZEBRA; + int type = -1; + int metric = -1; + struct ospf_redist *red; + int idx = 0; + int cur_originate = ospf->default_originate; + bool sameRtmap = false; + char *rtmap = NULL; + + red = ospf_redist_add(ospf, DEFAULT_ROUTE, 0); + + /* Check whether "always" was specified */ + if (argv_find(argv, argc, "always", &idx)) + default_originate = DEFAULT_ORIGINATE_ALWAYS; + idx = 1; + /* Get metric value */ + if (argv_find(argv, argc, "(0-16777214)", &idx)) { + if (!str2metric(argv[idx]->arg, &metric)) + return CMD_WARNING_CONFIG_FAILED; + } + idx = 1; + /* Get metric type. */ + if (argv_find(argv, argc, "(1-2)", &idx)) { + if (!str2metric_type(argv[idx]->arg, &type)) + return CMD_WARNING_CONFIG_FAILED; + } + idx = 1; + /* Get route-map */ + if (argv_find(argv, argc, "route-map", &idx)) + rtmap = argv[idx + 1]->arg; + + /* To check if user is providing same route map */ + if ((!rtmap && !ROUTEMAP_NAME(red)) || + (rtmap && ROUTEMAP_NAME(red) && + (strcmp(rtmap, ROUTEMAP_NAME(red)) == 0))) + sameRtmap = true; + + /* Don't allow if the same lsa is already originated. */ + if ((sameRtmap) + && (red->dmetric.type == type) + && (red->dmetric.value == metric) + && (cur_originate == default_originate)) + return CMD_SUCCESS; + + /* Updating Metric details */ + red->dmetric.type = type; + red->dmetric.value = metric; + + /* updating route map details */ + if (rtmap) + ospf_routemap_set(red, rtmap); + else + ospf_routemap_unset(red); + + return ospf_redistribute_default_set(ospf, default_originate, type, + metric); +} + +DEFUN (no_ospf_default_information_originate, + no_ospf_default_information_originate_cmd, + "no default-information originate [{always|metric (0-16777214)|metric-type (1-2)|route-map RMAP_NAME}]", + NO_STR + "Control distribution of default information\n" + "Distribute a default route\n" + "Always advertise default route\n" + "OSPF default metric\n" + "OSPF metric\n" + "OSPF metric type for default routes\n" + "Set OSPF External Type 1/2 metrics\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_redist *red; + + red = ospf_redist_lookup(ospf, DEFAULT_ROUTE, 0); + if (!red) + return CMD_SUCCESS; + + ospf_routemap_unset(red); + ospf_redist_del(ospf, DEFAULT_ROUTE, 0); + + return ospf_redistribute_default_set(ospf, DEFAULT_ORIGINATE_NONE, + 0, 0); +} + +DEFUN (ospf_default_metric, + ospf_default_metric_cmd, + "default-metric (0-16777214)", + "Set metric of redistributed routes\n" + "Default metric\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_number = 1; + int metric = -1; + + if (!str2metric(argv[idx_number]->arg, &metric)) + return CMD_WARNING_CONFIG_FAILED; + + ospf->default_metric = metric; + + ospf_schedule_asbr_redist_update(ospf); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_default_metric, + no_ospf_default_metric_cmd, + "no default-metric [(0-16777214)]", + NO_STR + "Set metric of redistributed routes\n" + "Default metric\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf->default_metric = -1; + + ospf_schedule_asbr_redist_update(ospf); + + return CMD_SUCCESS; +} + + +DEFUN (ospf_distance, + ospf_distance_cmd, + "distance (1-255)", + "Administrative distance\n" + "OSPF Administrative distance\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_number = 1; + uint8_t distance; + + distance = atoi(argv[idx_number]->arg); + if (ospf->distance_all != distance) { + ospf->distance_all = distance; + ospf_restart_spf(ospf); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_distance, + no_ospf_distance_cmd, + "no distance (1-255)", + NO_STR + "Administrative distance\n" + "OSPF Administrative distance\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + if (ospf->distance_all) { + ospf->distance_all = 0; + ospf_restart_spf(ospf); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_distance_ospf, + no_ospf_distance_ospf_cmd, + "no distance ospf [{intra-area [(1-255)]|inter-area [(1-255)]|external [(1-255)]}]", + NO_STR + "Administrative distance\n" + "OSPF administrative distance\n" + "Intra-area routes\n" + "Distance for intra-area routes\n" + "Inter-area routes\n" + "Distance for inter-area routes\n" + "External routes\n" + "Distance for external routes\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx = 0; + + if (argv_find(argv, argc, "intra-area", &idx) || argc == 3) + idx = ospf->distance_intra = 0; + if (argv_find(argv, argc, "inter-area", &idx) || argc == 3) + idx = ospf->distance_inter = 0; + if (argv_find(argv, argc, "external", &idx) || argc == 3) + ospf->distance_external = 0; + + return CMD_SUCCESS; +} + +DEFUN (ospf_distance_ospf, + ospf_distance_ospf_cmd, + "distance ospf {intra-area (1-255)|inter-area (1-255)|external (1-255)}", + "Administrative distance\n" + "OSPF administrative distance\n" + "Intra-area routes\n" + "Distance for intra-area routes\n" + "Inter-area routes\n" + "Distance for inter-area routes\n" + "External routes\n" + "Distance for external routes\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx = 0; + + ospf->distance_intra = 0; + ospf->distance_inter = 0; + ospf->distance_external = 0; + + if (argv_find(argv, argc, "intra-area", &idx)) + ospf->distance_intra = atoi(argv[idx + 1]->arg); + idx = 0; + if (argv_find(argv, argc, "inter-area", &idx)) + ospf->distance_inter = atoi(argv[idx + 1]->arg); + idx = 0; + if (argv_find(argv, argc, "external", &idx)) + ospf->distance_external = atoi(argv[idx + 1]->arg); + + return CMD_SUCCESS; +} + +DEFUN (ip_ospf_mtu_ignore, + ip_ospf_mtu_ignore_addr_cmd, + "ip ospf mtu-ignore [A.B.C.D]", + "IP Information\n" + "OSPF interface commands\n" + "Disable MTU mismatch detection on this interface\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_ipv4 = 3; + struct in_addr addr; + int ret; + + struct ospf_if_params *params; + params = IF_DEF_PARAMS(ifp); + + 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); + } + params->mtu_ignore = 1; + if (params->mtu_ignore != OSPF_MTU_IGNORE_DEFAULT) + SET_IF_PARAM(params, mtu_ignore); + else { + UNSET_IF_PARAM(params, mtu_ignore); + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, addr); + ospf_if_update_params(ifp, addr); + } + } + return CMD_SUCCESS; +} + +DEFUN (no_ip_ospf_mtu_ignore, + no_ip_ospf_mtu_ignore_addr_cmd, + "no ip ospf mtu-ignore [A.B.C.D]", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Disable MTU mismatch detection on this interface\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_ipv4 = 4; + struct in_addr addr; + int ret; + + struct ospf_if_params *params; + params = IF_DEF_PARAMS(ifp); + + 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_get_if_params(ifp, addr); + ospf_if_update_params(ifp, addr); + } + params->mtu_ignore = 0; + if (params->mtu_ignore != OSPF_MTU_IGNORE_DEFAULT) + SET_IF_PARAM(params, mtu_ignore); + else { + UNSET_IF_PARAM(params, mtu_ignore); + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, addr); + ospf_if_update_params(ifp, addr); + } + } + return CMD_SUCCESS; +} + +DEFPY(ip_ospf_capability_opaque, ip_ospf_capability_opaque_addr_cmd, + "[no] ip ospf capability opaque [A.B.C.D]$ip_addr", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Disable OSPF capability on this interface\n" + "Disable OSPF opaque LSA capability on this interface\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct route_node *rn; + bool old_opaque_capable; + bool opaque_capable_change; + + struct ospf_if_params *params; + params = IF_DEF_PARAMS(ifp); + + if (ip_addr.s_addr != INADDR_ANY) { + params = ospf_get_if_params(ifp, ip_addr); + ospf_if_update_params(ifp, ip_addr); + } + + old_opaque_capable = params->opaque_capable; + params->opaque_capable = (no) ? false : true; + opaque_capable_change = (old_opaque_capable != params->opaque_capable); + if (params->opaque_capable != OSPF_OPAQUE_CAPABLE_DEFAULT) + SET_IF_PARAM(params, opaque_capable); + else { + UNSET_IF_PARAM(params, opaque_capable); + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, ip_addr); + ospf_if_update_params(ifp, ip_addr); + } + } + + /* + * If there is a change to the opaque capability, flap the interface + * to reset all the neighbor adjacencies. + */ + if (opaque_capable_change) { + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi = rn->info; + + if (oi && (oi->state > ISM_Down) && + (ip_addr.s_addr == INADDR_ANY || + IPV4_ADDR_SAME(&oi->address->u.prefix4, + &ip_addr))) { + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown); + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp); + } + } + } + return CMD_SUCCESS; +} + + +DEFPY(ip_ospf_prefix_suppression, ip_ospf_prefix_suppression_addr_cmd, + "[no] ip ospf prefix-suppression [A.B.C.D]$ip_addr", NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Supress OSPF prefix advertisement on this interface\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct route_node *rn; + bool prefix_suppression_change; + struct ospf_if_params *params; + + params = IF_DEF_PARAMS(ifp); + + if (ip_addr.s_addr != INADDR_ANY) { + params = ospf_get_if_params(ifp, ip_addr); + ospf_if_update_params(ifp, ip_addr); + } + + prefix_suppression_change = (params->prefix_suppression == (bool)no); + params->prefix_suppression = (no) ? false : true; + if (params->prefix_suppression != OSPF_PREFIX_SUPPRESSION_DEFAULT) + SET_IF_PARAM(params, prefix_suppression); + else { + UNSET_IF_PARAM(params, prefix_suppression); + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, ip_addr); + ospf_if_update_params(ifp, ip_addr); + } + } + + /* + * If there is a change to the prefix suppression, update the Router-LSA. + */ + if (prefix_suppression_change) { + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi = rn->info; + + if (oi && (oi->state > ISM_Down) && + (ip_addr.s_addr == INADDR_ANY || + IPV4_ADDR_SAME(&oi->address->u.prefix4, &ip_addr))) { + (void)ospf_router_lsa_update_area(oi->area); + if (oi->state == ISM_DR) + ospf_network_lsa_update(oi); + } + } + } + return CMD_SUCCESS; +} + +DEFUN (ospf_max_metric_router_lsa_admin, + ospf_max_metric_router_lsa_admin_cmd, + "max-metric router-lsa administrative", + "OSPF maximum / infinite-distance metric\n" + "Advertise own Router-LSA with infinite distance (stub router)\n" + "Administratively applied, for an indefinite period\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct listnode *ln; + struct ospf_area *area; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, ln, area)) { + SET_FLAG(area->stub_router_state, OSPF_AREA_ADMIN_STUB_ROUTED); + + if (!CHECK_FLAG(area->stub_router_state, + OSPF_AREA_IS_STUB_ROUTED)) + ospf_router_lsa_update_area(area); + } + + /* Allows for areas configured later to get the property */ + ospf->stub_router_admin_set = OSPF_STUB_ROUTER_ADMINISTRATIVE_SET; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_max_metric_router_lsa_admin, + no_ospf_max_metric_router_lsa_admin_cmd, + "no max-metric router-lsa administrative", + NO_STR + "OSPF maximum / infinite-distance metric\n" + "Advertise own Router-LSA with infinite distance (stub router)\n" + "Administratively applied, for an indefinite period\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct listnode *ln; + struct ospf_area *area; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, ln, area)) { + UNSET_FLAG(area->stub_router_state, + OSPF_AREA_ADMIN_STUB_ROUTED); + + /* Don't trample on the start-up stub timer */ + if (CHECK_FLAG(area->stub_router_state, + OSPF_AREA_IS_STUB_ROUTED) + && !area->t_stub_router) { + UNSET_FLAG(area->stub_router_state, + OSPF_AREA_IS_STUB_ROUTED); + ospf_router_lsa_update_area(area); + } + } + ospf->stub_router_admin_set = OSPF_STUB_ROUTER_ADMINISTRATIVE_UNSET; + return CMD_SUCCESS; +} + +DEFUN (ospf_max_metric_router_lsa_startup, + ospf_max_metric_router_lsa_startup_cmd, + "max-metric router-lsa on-startup (5-86400)", + "OSPF maximum / infinite-distance metric\n" + "Advertise own Router-LSA with infinite distance (stub router)\n" + "Automatically advertise stub Router-LSA on startup of OSPF\n" + "Time (seconds) to advertise self as stub-router\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_number = 3; + unsigned int seconds; + + if (argc < 4) { + vty_out(vty, "%% Must supply stub-router period\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + seconds = strtoul(argv[idx_number]->arg, NULL, 10); + + ospf->stub_router_startup_time = seconds; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_max_metric_router_lsa_startup, + no_ospf_max_metric_router_lsa_startup_cmd, + "no max-metric router-lsa on-startup [(5-86400)]", + NO_STR + "OSPF maximum / infinite-distance metric\n" + "Advertise own Router-LSA with infinite distance (stub router)\n" + "Automatically advertise stub Router-LSA on startup of OSPF\n" + "Time (seconds) to advertise self as stub-router\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct listnode *ln; + struct ospf_area *area; + + ospf->stub_router_startup_time = OSPF_STUB_ROUTER_UNCONFIGURED; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, ln, area)) { + SET_FLAG(area->stub_router_state, + OSPF_AREA_WAS_START_STUB_ROUTED); + EVENT_OFF(area->t_stub_router); + + /* Don't trample on admin stub routed */ + if (!CHECK_FLAG(area->stub_router_state, + OSPF_AREA_ADMIN_STUB_ROUTED)) { + UNSET_FLAG(area->stub_router_state, + OSPF_AREA_IS_STUB_ROUTED); + ospf_router_lsa_update_area(area); + } + } + return CMD_SUCCESS; +} + + +DEFUN (ospf_max_metric_router_lsa_shutdown, + ospf_max_metric_router_lsa_shutdown_cmd, + "max-metric router-lsa on-shutdown (5-100)", + "OSPF maximum / infinite-distance metric\n" + "Advertise own Router-LSA with infinite distance (stub router)\n" + "Advertise stub-router prior to full shutdown of OSPF\n" + "Time (seconds) to wait till full shutdown\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + int idx_number = 3; + unsigned int seconds; + + if (argc < 4) { + vty_out(vty, "%% Must supply stub-router shutdown period\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + seconds = strtoul(argv[idx_number]->arg, NULL, 10); + + ospf->stub_router_shutdown_time = seconds; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_max_metric_router_lsa_shutdown, + no_ospf_max_metric_router_lsa_shutdown_cmd, + "no max-metric router-lsa on-shutdown [(5-100)]", + NO_STR + "OSPF maximum / infinite-distance metric\n" + "Advertise own Router-LSA with infinite distance (stub router)\n" + "Advertise stub-router prior to full shutdown of OSPF\n" + "Time (seconds) to wait till full shutdown\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf->stub_router_shutdown_time = OSPF_STUB_ROUTER_UNCONFIGURED; + + return CMD_SUCCESS; +} + +DEFUN (ospf_proactive_arp, + ospf_proactive_arp_cmd, + "proactive-arp", + "Allow sending ARP requests proactively\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf->proactive_arp = true; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_proactive_arp, + no_ospf_proactive_arp_cmd, + "no proactive-arp", + NO_STR + "Disallow sending ARP requests proactively\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf->proactive_arp = false; + + return CMD_SUCCESS; +} + +/* Graceful Restart HELPER Commands */ +DEFPY(ospf_gr_helper_enable, ospf_gr_helper_enable_cmd, + "graceful-restart helper enable [A.B.C.D$address]", + "OSPF Graceful Restart\n" + "OSPF GR Helper\n" + "Enable Helper support\n" + "Advertising Router-ID\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + if (address_str) { + ospf_gr_helper_support_set_per_routerid(ospf, &address, + OSPF_GR_TRUE); + return CMD_SUCCESS; + } + + ospf_gr_helper_support_set(ospf, OSPF_GR_TRUE); + + return CMD_SUCCESS; +} + +DEFPY(no_ospf_gr_helper_enable, + no_ospf_gr_helper_enable_cmd, + "no graceful-restart helper enable [A.B.C.D$address]", + NO_STR + "OSPF Graceful Restart\n" + "OSPF GR Helper\n" + "Enable Helper support\n" + "Advertising Router-ID\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + if (address_str) { + ospf_gr_helper_support_set_per_routerid(ospf, &address, + OSPF_GR_FALSE); + return CMD_SUCCESS; + } + + ospf_gr_helper_support_set(ospf, OSPF_GR_FALSE); + return CMD_SUCCESS; +} + +DEFPY(ospf_gr_helper_enable_lsacheck, + ospf_gr_helper_enable_lsacheck_cmd, + "graceful-restart helper strict-lsa-checking", + "OSPF Graceful Restart\n" + "OSPF GR Helper\n" + "Enable strict LSA check\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf_gr_helper_lsa_check_set(ospf, OSPF_GR_TRUE); + return CMD_SUCCESS; +} + +DEFPY(no_ospf_gr_helper_enable_lsacheck, + no_ospf_gr_helper_enable_lsacheck_cmd, + "no graceful-restart helper strict-lsa-checking", + NO_STR + "OSPF Graceful Restart\n" + "OSPF GR Helper\n" + "Disable strict LSA check\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf_gr_helper_lsa_check_set(ospf, OSPF_GR_FALSE); + return CMD_SUCCESS; +} + +DEFPY(ospf_gr_helper_supported_grace_time, + ospf_gr_helper_supported_grace_time_cmd, + "graceful-restart helper supported-grace-time (10-1800)$interval", + "OSPF Graceful Restart\n" + "OSPF GR Helper\n" + "Supported grace timer\n" + "Grace interval(in seconds)\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf_gr_helper_supported_gracetime_set(ospf, interval); + return CMD_SUCCESS; +} + +DEFPY(no_ospf_gr_helper_supported_grace_time, + no_ospf_gr_helper_supported_grace_time_cmd, + "no graceful-restart helper supported-grace-time (10-1800)$interval", + NO_STR + "OSPF Graceful Restart\n" + "OSPF GR Helper\n" + "Supported grace timer\n" + "Grace interval(in seconds)\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf_gr_helper_supported_gracetime_set(ospf, OSPF_MAX_GRACE_INTERVAL); + return CMD_SUCCESS; +} + +DEFPY(ospf_gr_helper_planned_only, + ospf_gr_helper_planned_only_cmd, + "graceful-restart helper planned-only", + "OSPF Graceful Restart\n" + "OSPF GR Helper\n" + "Supported only planned restart\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf_gr_helper_set_supported_planned_only_restart(ospf, OSPF_GR_TRUE); + + return CMD_SUCCESS; +} + +/* External Route Aggregation */ +DEFUN (ospf_external_route_aggregation, + ospf_external_route_aggregation_cmd, + "summary-address A.B.C.D/M [tag (1-4294967295)]", + "External summary address\n" + "Summary address prefix\n" + "Router tag \n" + "Router tag value\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct prefix_ipv4 p; + int idx = 1; + route_tag_t tag = 0; + int ret = OSPF_SUCCESS; + + str2prefix_ipv4(argv[idx]->arg, &p); + + if (is_default_prefix4(&p)) { + vty_out(vty, + "Default address shouldn't be configured as summary address.\n"); + return CMD_SUCCESS; + } + + /* Apply mask for given prefix. */ + apply_mask(&p); + + if (!is_valid_summary_addr(&p)) { + vty_out(vty, "Not a valid summary address.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (argc > 2) + tag = strtoul(argv[idx + 2]->arg, NULL, 10); + + ret = ospf_asbr_external_aggregator_set(ospf, &p, tag); + if (ret == OSPF_INVALID) + vty_out(vty, "Invalid configuration!!\n"); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_external_route_aggregation, + no_ospf_external_route_aggregation_cmd, + "no summary-address A.B.C.D/M [tag (1-4294967295)]", + NO_STR + "External summary address\n" + "Summary address prefix\n" + "Router tag\n" + "Router tag value\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct prefix_ipv4 p; + int idx = 2; + route_tag_t tag = 0; + int ret = OSPF_SUCCESS; + + str2prefix_ipv4(argv[idx]->arg, &p); + + if (is_default_prefix4(&p)) { + vty_out(vty, + "Default address shouldn't be configured as summary address.\n"); + return CMD_SUCCESS; + } + + /* Apply mask for given prefix. */ + apply_mask(&p); + + if (!is_valid_summary_addr(&p)) { + vty_out(vty, "Not a valid summary address.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (argc > 3) + tag = strtoul(argv[idx + 2]->arg, NULL, 10); + + ret = ospf_asbr_external_aggregator_unset(ospf, &p, tag); + if (ret == OSPF_INVALID) + vty_out(vty, "Invalid configuration!!\n"); + + return CMD_SUCCESS; +} + +DEFPY(no_ospf_gr_helper_planned_only, + no_ospf_gr_helper_planned_only_cmd, + "no graceful-restart helper planned-only", + NO_STR + "OSPF Graceful Restart\n" + "OSPF GR Helper\n" + "Supported only for planned restart\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf_gr_helper_set_supported_planned_only_restart(ospf, OSPF_GR_FALSE); + + return CMD_SUCCESS; +} + +static int ospf_print_vty_helper_dis_rtr_walkcb(struct hash_bucket *bucket, + void *arg) +{ + struct advRtr *rtr = bucket->data; + struct vty *vty = (struct vty *)arg; + static unsigned int count; + + vty_out(vty, "%-6pI4,", &rtr->advRtrAddr); + count++; + + if (count % 5 == 0) + vty_out(vty, "\n"); + + return HASHWALK_CONTINUE; +} + +static int ospf_print_json_helper_enabled_rtr_walkcb(struct hash_bucket *bucket, + void *arg) +{ + struct advRtr *rtr = bucket->data; + struct json_object *json_rid_array = arg; + struct json_object *json_rid; + + json_rid = json_object_new_object(); + + json_object_string_addf(json_rid, "routerId", "%pI4", &rtr->advRtrAddr); + json_object_array_add(json_rid_array, json_rid); + + return HASHWALK_CONTINUE; +} + +static int ospf_show_gr_helper_details(struct vty *vty, struct ospf *ospf, + uint8_t use_vrf, json_object *json, + bool uj, bool detail) +{ + struct listnode *node; + struct ospf_interface *oi; + char buf[PREFIX_STRLEN]; + json_object *json_vrf = NULL; + + if (uj) { + if (use_vrf) + json_vrf = json_object_new_object(); + else + json_vrf = json; + } + + if (ospf->instance) { + if (uj) + json_object_int_add(json, "ospfInstance", + ospf->instance); + else + vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); + } + + ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf); + + if (uj) { + if (use_vrf) + json_object_object_add(json, ospf_get_name(ospf), + json_vrf); + } else + vty_out(vty, "\n"); + + /* Show Router ID. */ + if (uj) { + json_object_string_add(json_vrf, "routerId", + inet_ntop(AF_INET, &ospf->router_id, + buf, sizeof(buf))); + } else { + vty_out(vty, "\n OSPF Router with ID (%pI4)\n\n", + &ospf->router_id); + } + + if (!uj) { + + if (ospf->is_helper_supported) + vty_out(vty, + " Graceful restart helper support enabled.\n"); + else + vty_out(vty, + " Graceful restart helper support disabled.\n"); + + if (ospf->strict_lsa_check) + vty_out(vty, " Strict LSA check is enabled.\n"); + else + vty_out(vty, " Strict LSA check is disabled.\n"); + + if (ospf->only_planned_restart) + vty_out(vty, + " Helper supported for planned restarts only.\n"); + else + vty_out(vty, + " Helper supported for Planned and Unplanned Restarts.\n"); + + vty_out(vty, + " Supported Graceful restart interval: %d(in seconds).\n", + ospf->supported_grace_time); + + if (OSPF_HELPER_ENABLE_RTR_COUNT(ospf)) { + vty_out(vty, " Enable Router list:\n"); + vty_out(vty, " "); + hash_walk(ospf->enable_rtr_list, + ospf_print_vty_helper_dis_rtr_walkcb, vty); + vty_out(vty, "\n\n"); + } + + if (ospf->last_exit_reason != OSPF_GR_HELPER_EXIT_NONE) { + vty_out(vty, " Last Helper exit Reason :%s\n", + ospf_exit_reason2str(ospf->last_exit_reason)); + } + + if (ospf->active_restarter_cnt) + vty_out(vty, + " Number of Active neighbours in graceful restart: %d\n", + ospf->active_restarter_cnt); + else + vty_out(vty, "\n"); + + } else { + json_object_string_add( + json_vrf, "helperSupport", + (ospf->is_helper_supported) ? "Enabled" : "Disabled"); + json_object_string_add(json_vrf, "strictLsaCheck", + (ospf->strict_lsa_check) ? "Enabled" + : "Disabled"); +#if CONFDATE > 20240401 + CPP_NOTICE("Remove deprecated json key: restartSupoort") +#endif + json_object_string_add( + json_vrf, "restartSupoort", + (ospf->only_planned_restart) + ? "Planned Restart only" + : "Planned and Unplanned Restarts"); + + json_object_string_add( + json_vrf, "restartSupport", + (ospf->only_planned_restart) + ? "Planned Restart only" + : "Planned and Unplanned Restarts"); + + json_object_int_add(json_vrf, "supportedGracePeriod", + ospf->supported_grace_time); + + if (ospf->last_exit_reason != OSPF_GR_HELPER_EXIT_NONE) + json_object_string_add( + json_vrf, "lastExitReason", + ospf_exit_reason2str(ospf->last_exit_reason)); + + if (ospf->active_restarter_cnt) + json_object_int_add(json_vrf, "activeRestarterCnt", + ospf->active_restarter_cnt); + + if (OSPF_HELPER_ENABLE_RTR_COUNT(ospf)) { + struct json_object *json_rid_array = + json_object_new_array(); + + json_object_object_add(json_vrf, "enabledRouterIds", + json_rid_array); + + hash_walk(ospf->enable_rtr_list, + ospf_print_json_helper_enabled_rtr_walkcb, + json_rid_array); + } + } + + + if (detail) { + int cnt = 1; + json_object *json_neighbors = NULL; + + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + struct route_node *rn; + struct ospf_neighbor *nbr; + json_object *json_neigh; + + if (ospf_interface_neighbor_count(oi) == 0) + continue; + + if (uj) { + json_object_object_get_ex(json_vrf, "neighbors", + &json_neighbors); + if (!json_neighbors) { + json_neighbors = + json_object_new_object(); + json_object_object_add(json_vrf, + "neighbors", + json_neighbors); + } + } + + for (rn = route_top(oi->nbrs); rn; + rn = route_next(rn)) { + + if (!rn->info) + continue; + + nbr = rn->info; + + if (!OSPF_GR_IS_ACTIVE_HELPER(nbr)) + continue; + + if (!uj) { + vty_out(vty, " Neighbour %d :\n", cnt); + vty_out(vty, " Address : %pI4\n", + &nbr->address.u.prefix4); + vty_out(vty, " Routerid : %pI4\n", + &nbr->router_id); + vty_out(vty, + " Received Grace period : %d(in seconds).\n", + nbr->gr_helper_info + .recvd_grace_period); + vty_out(vty, + " Actual Grace period : %d(in seconds)\n", + nbr->gr_helper_info + .actual_grace_period); + vty_out(vty, + " Remaining GraceTime:%ld(in seconds).\n", + event_timer_remain_second( + nbr->gr_helper_info + .t_grace_timer)); + vty_out(vty, + " Graceful Restart reason: %s.\n\n", + ospf_restart_reason2str( + nbr->gr_helper_info + .gr_restart_reason)); + cnt++; + } else { + json_neigh = json_object_new_object(); + json_object_string_add( + json_neigh, "srcAddr", + inet_ntop(AF_INET, &nbr->src, + buf, sizeof(buf))); + + json_object_string_add( + json_neigh, "routerid", + inet_ntop(AF_INET, + &nbr->router_id, + buf, sizeof(buf))); + json_object_int_add( + json_neigh, + "recvdGraceInterval", + nbr->gr_helper_info + .recvd_grace_period); + json_object_int_add( + json_neigh, + "actualGraceInterval", + nbr->gr_helper_info + .actual_grace_period); + json_object_int_add( + json_neigh, "remainGracetime", + event_timer_remain_second( + nbr->gr_helper_info + .t_grace_timer)); + json_object_string_add( + json_neigh, "restartReason", + ospf_restart_reason2str( + nbr->gr_helper_info + .gr_restart_reason)); + json_object_object_add( + json_neighbors, + inet_ntop(AF_INET, &nbr->src, + buf, sizeof(buf)), + json_neigh); + } + } + } + } + return CMD_SUCCESS; +} + +DEFUN (ospf_external_route_aggregation_no_adrvertise, + ospf_external_route_aggregation_no_adrvertise_cmd, + "summary-address A.B.C.D/M no-advertise", + "External summary address\n" + "Summary address prefix\n" + "Don't advertise summary route \n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct prefix_ipv4 p; + int idx = 1; + int ret = OSPF_SUCCESS; + + str2prefix_ipv4(argv[idx]->arg, &p); + + if (is_default_prefix4(&p)) { + vty_out(vty, + "Default address shouldn't be configured as summary address.\n"); + return CMD_SUCCESS; + } + + /* Apply mask for given prefix. */ + apply_mask(&p); + + if (!is_valid_summary_addr(&p)) { + vty_out(vty, "Not a valid summary address.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ret = ospf_asbr_external_rt_no_advertise(ospf, &p); + if (ret == OSPF_INVALID) + vty_out(vty, "Invalid configuration!!\n"); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_external_route_aggregation_no_adrvertise, + no_ospf_external_route_aggregation_no_adrvertise_cmd, + "no summary-address A.B.C.D/M no-advertise", + NO_STR + "External summary address\n" + "Summary address prefix\n" + "Advertise summary route to the AS \n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct prefix_ipv4 p; + int idx = 2; + int ret = OSPF_SUCCESS; + + str2prefix_ipv4(argv[idx]->arg, &p); + + if (is_default_prefix4(&p)) { + vty_out(vty, + "Default address shouldn't be configured as summary address.\n"); + return CMD_SUCCESS; + } + + /* Apply mask for given prefix. */ + apply_mask(&p); + + if (!is_valid_summary_addr(&p)) { + vty_out(vty, "Not a valid summary address.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ret = ospf_asbr_external_rt_advertise(ospf, &p); + if (ret == OSPF_INVALID) + vty_out(vty, "Invalid configuration!!\n"); + + return CMD_SUCCESS; +} + +DEFUN (ospf_route_aggregation_timer, + ospf_route_aggregation_timer_cmd, + "aggregation timer (5-1800)", + "External route aggregation\n" + "Delay timer (in seconds)\n" + "Timer interval(in seconds)\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + uint16_t interval = 0; + + interval = strtoul(argv[2]->arg, NULL, 10); + + ospf_external_aggregator_timer_set(ospf, interval); + + return CMD_SUCCESS; +} + +DEFPY (show_ip_ospf_gr_helper, + show_ip_ospf_gr_helper_cmd, + "show ip ospf [vrf ] graceful-restart helper [detail] [json]", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "All VRFs\n" + "OSPF Graceful Restart\n" + "Helper details in the router\n" + "Detailed information\n" + JSON_STR) +{ + char *vrf_name = NULL; + bool all_vrf = false; + int ret = CMD_SUCCESS; + int idx_vrf = 0; + int idx = 0; + uint8_t use_vrf = 0; + bool uj = use_json(argc, argv); + struct ospf *ospf = NULL; + json_object *json = NULL; + struct listnode *node = NULL; + int inst = 0; + bool detail = false; + + OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + if (argv_find(argv, argc, "detail", &idx)) + detail = true; + + if (uj) + json = json_object_new_object(); + + /* vrf input is provided */ + if (vrf_name) { + use_vrf = 1; + + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + + ret = ospf_show_gr_helper_details( + vty, ospf, use_vrf, json, uj, detail); + } + + if (uj) + vty_json(vty, json); + + return ret; + } + + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + } else { + /* Default Vrf */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + } + + if (ospf == NULL || !ospf->oi_running) { + + if (uj) + vty_json(vty, json); + else + vty_out(vty, + "%% OSPF is not enabled in vrf %s\n", vrf_name ? vrf_name : "default"); + + return CMD_SUCCESS; + } + + ospf_show_gr_helper_details(vty, ospf, use_vrf, json, uj, detail); + if (uj) + vty_json(vty, json); + + return CMD_SUCCESS; +} +/* Graceful Restart HELPER commands end */ +DEFUN (no_ospf_route_aggregation_timer, + no_ospf_route_aggregation_timer_cmd, + "no aggregation timer", + NO_STR + "External route aggregation\n" + "Delay timer\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + + ospf_external_aggregator_timer_set(ospf, OSPF_EXTL_AGGR_DEFAULT_DELAY); + + return CMD_SUCCESS; +} + +/* External Route Aggregation End */ + +static void config_write_stub_router(struct vty *vty, struct ospf *ospf) +{ + if (ospf->stub_router_startup_time != OSPF_STUB_ROUTER_UNCONFIGURED) + vty_out(vty, " max-metric router-lsa on-startup %u\n", + ospf->stub_router_startup_time); + if (ospf->stub_router_shutdown_time != OSPF_STUB_ROUTER_UNCONFIGURED) + vty_out(vty, " max-metric router-lsa on-shutdown %u\n", + ospf->stub_router_shutdown_time); + if (ospf->stub_router_admin_set == OSPF_STUB_ROUTER_ADMINISTRATIVE_SET) + vty_out(vty, " max-metric router-lsa administrative\n"); + + return; +} + +static void show_ip_ospf_route_network(struct vty *vty, struct ospf *ospf, + struct route_table *rt, + json_object *json, bool detail) +{ + struct route_node *rn; + struct ospf_route * or ; + struct listnode *pnode, *pnnode; + struct ospf_path *path; + json_object *json_route = NULL, *json_nexthop_array = NULL, + *json_nexthop = NULL; + + if (!json) + vty_out(vty, + "============ OSPF network routing table ============\n"); + + for (rn = route_top(rt); rn; rn = route_next(rn)) { + char buf1[PREFIX2STR_BUFFER]; + + if ((or = rn->info) == NULL) + continue; + + prefix2str(&rn->p, buf1, sizeof(buf1)); + + if (json) { + json_route = json_object_new_object(); + json_object_object_add(json, buf1, json_route); + } + + switch (or->path_type) { + case OSPF_PATH_INTER_AREA: + if (or->type == OSPF_DESTINATION_NETWORK) { + if (json) { + json_object_string_add(json_route, + "routeType", + "N IA"); + json_object_int_add(json_route, "cost", + or->cost); + json_object_string_addf( + json_route, "area", "%pI4", + &or->u.std.area_id); + } else { + vty_out(vty, + "N IA %-18s [%d] area: %pI4\n", + buf1, or->cost, + &or->u.std.area_id); + } + } else if (or->type == OSPF_DESTINATION_DISCARD) { + if (json) { + json_object_string_add(json_route, + "routeType", + "D IA"); + } else { + vty_out(vty, + "D IA %-18s Discard entry\n", + buf1); + } + } + break; + case OSPF_PATH_INTRA_AREA: + if (json) { + json_object_string_add(json_route, "routeType", + "N"); + json_object_boolean_add(json_route, "transit", + or->u.std.transit); + json_object_int_add(json_route, "cost", + or->cost); + json_object_string_addf(json_route, "area", + "%pI4", + &or->u.std.area_id); + } else { + vty_out(vty, "N %s %-18s [%d] area: %pI4\n", + or->u.std.transit && detail ? "T" : " ", + buf1, or->cost, &or->u.std.area_id); + } + break; + default: + break; + } + + if (or->type == OSPF_DESTINATION_NETWORK) { + if (json) { + json_nexthop_array = json_object_new_array(); + json_object_object_add(json_route, "nexthops", + json_nexthop_array); + } + + for (ALL_LIST_ELEMENTS(or->paths, pnode, pnnode, + path)) { + if (json) { + json_nexthop = json_object_new_object(); + json_object_array_add( + json_nexthop_array, + json_nexthop); + } + if (if_lookup_by_index(path->ifindex, + ospf->vrf_id)) { + + if (path->nexthop.s_addr + == INADDR_ANY) { + if (json) { + json_object_string_add( + json_nexthop, + "ip", " "); + json_object_string_add( + json_nexthop, + "directlyAttachedTo", + ifindex2ifname( + path->ifindex, + ospf->vrf_id)); + } else { + vty_out(vty, + "%24s directly attached to %s\n", + "", + ifindex2ifname( + path->ifindex, + ospf->vrf_id)); + } + } else { + if (json) { + json_object_string_addf( + json_nexthop, + "ip", "%pI4", + &path->nexthop); + json_object_string_add( + json_nexthop, + "via", + ifindex2ifname( + path->ifindex, + ospf->vrf_id)); + json_object_string_addf( + json_nexthop, + "advertisedRouter", + "%pI4", + &path->adv_router); + } else { + vty_out(vty, + "%24s via %pI4, %s\n", + "", + &path->nexthop, + ifindex2ifname( + path->ifindex, + ospf->vrf_id)); + } + if (detail && !json) + vty_out(vty, + "%24s adv %pI4\n", + "", + &path->adv_router); + } + } + } + } + } + if (!json) + vty_out(vty, "\n"); +} + +static void show_ip_ospf_route_router(struct vty *vty, struct ospf *ospf, + struct route_table *rtrs, + json_object *json) +{ + struct route_node *rn; + struct ospf_route * or ; + struct listnode *pnode; + struct listnode *node; + struct ospf_path *path; + char buf[PREFIX_STRLEN]; + json_object *json_route = NULL, *json_nexthop_array = NULL, + *json_nexthop = NULL; + + if (!json) + vty_out(vty, "============ OSPF %s table =============\n", + ospf->all_rtrs == rtrs ? "reachable routers" + : "router routing"); + + for (rn = route_top(rtrs); rn; rn = route_next(rn)) { + if (rn->info == NULL) + continue; + int flag = 0; + + if (json) { + json_route = json_object_new_object(); + json_object_object_add( + json, inet_ntop(AF_INET, &rn->p.u.prefix4, + buf, sizeof(buf)), + json_route); + json_object_string_add(json_route, "routeType", "R "); + } else { + vty_out(vty, "R %-15pI4 ", + &rn->p.u.prefix4); + } + + for (ALL_LIST_ELEMENTS_RO((struct list *)rn->info, node, or)) { + if (flag++) { + if (!json) + vty_out(vty, "%24s", ""); + } + + /* Show path. */ + if (json) { + json_object_int_add(json_route, "cost", + or->cost); + json_object_string_addf(json_route, "area", + "%pI4", + &or->u.std.area_id); + if (or->path_type == OSPF_PATH_INTER_AREA) { + json_object_boolean_true_add(json_route, + "IA"); + json_object_boolean_true_add(json_route, + "ia"); + } + if (or->u.std.flags & ROUTER_LSA_BORDER) + json_object_string_add(json_route, + "routerType", + "abr"); + else if (or->u.std.flags & ROUTER_LSA_EXTERNAL) + json_object_string_add(json_route, + "routerType", + "asbr"); + } else { + vty_out(vty, "%s [%d] area: %pI4", + (or->path_type == OSPF_PATH_INTER_AREA + ? "IA" + : " "), + or->cost, &or->u.std.area_id); + /* Show flags. */ + vty_out(vty, "%s%s\n", + (or->u.std.flags & ROUTER_LSA_BORDER + ? ", ABR" + : ""), + (or->u.std.flags & ROUTER_LSA_EXTERNAL + ? ", ASBR" + : "")); + } + + if (json) { + json_nexthop_array = json_object_new_array(); + json_object_object_add(json_route, "nexthops", + json_nexthop_array); + } + + for (ALL_LIST_ELEMENTS_RO(or->paths, pnode, path)) { + if (json) { + json_nexthop = json_object_new_object(); + json_object_array_add( + json_nexthop_array, + json_nexthop); + } + if (if_lookup_by_index(path->ifindex, + ospf->vrf_id)) { + if (path->nexthop.s_addr + == INADDR_ANY) { + if (json) { + json_object_string_add( + json_nexthop, + "ip", " "); + json_object_string_add( + json_nexthop, + "directlyAttachedTo", + ifindex2ifname( + path->ifindex, + ospf->vrf_id)); + } else { + vty_out(vty, + "%24s directly attached to %s\n", + "", + ifindex2ifname( + path->ifindex, + ospf->vrf_id)); + } + } else { + if (json) { + json_object_string_addf( + json_nexthop, + "ip", "%pI4", + &path->nexthop); + json_object_string_add( + json_nexthop, + "via", + ifindex2ifname( + path->ifindex, + ospf->vrf_id)); + } else { + vty_out(vty, + "%24s via %pI4, %s\n", + "", + &path->nexthop, + ifindex2ifname( + path->ifindex, + ospf->vrf_id)); + } + } + } + } + } + } + if (!json) + vty_out(vty, "\n"); +} + +static void show_ip_ospf_route_external(struct vty *vty, struct ospf *ospf, + struct route_table *rt, + json_object *json, bool detail) +{ + struct route_node *rn; + struct ospf_route *er; + struct listnode *pnode, *pnnode; + struct ospf_path *path; + json_object *json_route = NULL, *json_nexthop_array = NULL, + *json_nexthop = NULL; + + if (!json) + vty_out(vty, + "============ OSPF external routing table ===========\n"); + + for (rn = route_top(rt); rn; rn = route_next(rn)) { + if ((er = rn->info) == NULL) + continue; + + char buf1[19]; + + snprintfrr(buf1, sizeof(buf1), "%pFX", &rn->p); + if (json) { + json_route = json_object_new_object(); + json_object_object_add(json, buf1, json_route); + } + + switch (er->path_type) { + case OSPF_PATH_TYPE1_EXTERNAL: + if (json) { + json_object_string_add(json_route, "routeType", + "N E1"); + json_object_int_add(json_route, "cost", + er->cost); + json_object_int_add(json_route, "tag", + er->u.ext.tag); + } else { + vty_out(vty, + "N E1 %-18s [%d] tag: %" ROUTE_TAG_PRI + "\n", + buf1, er->cost, er->u.ext.tag); + } + break; + case OSPF_PATH_TYPE2_EXTERNAL: + if (json) { + json_object_string_add(json_route, "routeType", + "N E2"); + json_object_int_add(json_route, "cost", + er->cost); + json_object_int_add(json_route, "type2cost", + er->u.ext.type2_cost); + json_object_int_add(json_route, "tag", + er->u.ext.tag); + } else { + vty_out(vty, + "N E2 %-18s [%d/%d] tag: %" ROUTE_TAG_PRI + "\n", + buf1, er->cost, er->u.ext.type2_cost, + er->u.ext.tag); + } + break; + } + + if (json) { + json_nexthop_array = json_object_new_array(); + json_object_object_add(json_route, "nexthops", + json_nexthop_array); + } + + for (ALL_LIST_ELEMENTS(er->paths, pnode, pnnode, path)) { + if (json) { + json_nexthop = json_object_new_object(); + json_object_array_add(json_nexthop_array, + json_nexthop); + } + + if (if_lookup_by_index(path->ifindex, ospf->vrf_id)) { + if (path->nexthop.s_addr == INADDR_ANY) { + if (json) { + json_object_string_add( + json_nexthop, "ip", + " "); + json_object_string_add( + json_nexthop, + "directlyAttachedTo", + ifindex2ifname( + path->ifindex, + ospf->vrf_id)); + } else { + vty_out(vty, + "%24s directly attached to %s\n", + "", + ifindex2ifname( + path->ifindex, + ospf->vrf_id)); + } + } else { + if (json) { + json_object_string_addf( + json_nexthop, "ip", + "%pI4", &path->nexthop); + json_object_string_add( + json_nexthop, "via", + ifindex2ifname( + path->ifindex, + ospf->vrf_id)); + json_object_string_addf( + json_nexthop, + "advertisedRouter", + "%pI4", + &path->adv_router); + } else { + vty_out(vty, + "%24s via %pI4, %s\n", + "", + &path->nexthop, + ifindex2ifname( + path->ifindex, + ospf->vrf_id)); + } + if (detail && !json) + vty_out(vty, + "%24s adv %pI4\n", "", + &path->adv_router); + } + } + } + } + if (!json) + vty_out(vty, "\n"); +} + +static int show_ip_ospf_reachable_routers_common(struct vty *vty, + struct ospf *ospf, + uint8_t use_vrf) +{ + if (ospf->instance) + vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); + + ospf_show_vrf_name(ospf, vty, NULL, use_vrf); + + if (ospf->all_rtrs == NULL) { + vty_out(vty, "No OSPF reachable router information exist\n"); + return CMD_SUCCESS; + } + + /* Show Router routes. */ + show_ip_ospf_route_router(vty, ospf, ospf->all_rtrs, NULL); + + vty_out(vty, "\n"); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_reachable_routers, + show_ip_ospf_reachable_routers_cmd, + "show ip ospf [vrf ] reachable-routers", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "All VRFs\n" + "Show all the reachable OSPF routers\n") +{ + struct ospf *ospf = NULL; + struct listnode *node = NULL; + char *vrf_name = NULL; + bool all_vrf = false; + int ret = CMD_SUCCESS; + int inst = 0; + int idx_vrf = 0; + uint8_t use_vrf = 0; + + OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + if (vrf_name) { + bool ospf_output = false; + + use_vrf = 1; + + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + + ospf_output = true; + ret = show_ip_ospf_reachable_routers_common( + vty, ospf, use_vrf); + } + + if (!ospf_output) + vty_out(vty, "%% OSPF instance not found\n"); + } else { + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + if (ospf == NULL || !ospf->oi_running) { + vty_out(vty, "%% OSPF instance not found\n"); + return CMD_SUCCESS; + } + + ret = show_ip_ospf_reachable_routers_common(vty, ospf, + use_vrf); + } + } else { + /* Display default ospf (instance 0) info */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || !ospf->oi_running) { + vty_out(vty, "%% OSPF instance not found\n"); + return CMD_SUCCESS; + } + + ret = show_ip_ospf_reachable_routers_common(vty, ospf, use_vrf); + } + + return ret; +} + +DEFUN (show_ip_ospf_instance_reachable_routers, + show_ip_ospf_instance_reachable_routers_cmd, + "show ip ospf (1-65535) reachable-routers", + SHOW_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + "Show all the reachable OSPF routers\n") +{ + int idx_number = 3; + struct ospf *ospf; + unsigned short instance = 0; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + ospf = ospf_lookup_instance(instance); + if (!ospf || !ospf->oi_running) + return CMD_SUCCESS; + + return show_ip_ospf_reachable_routers_common(vty, ospf, 0); +} + +static int show_ip_ospf_border_routers_common(struct vty *vty, + struct ospf *ospf, + uint8_t use_vrf, + json_object *json) +{ + json_object *json_vrf = NULL; + json_object *json_router = NULL; + + if (json) { + if (use_vrf) + json_vrf = json_object_new_object(); + else + json_vrf = json; + json_router = json_object_new_object(); + } + + if (ospf->instance) { + if (!json) + vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); + else + json_object_int_add(json_vrf, "ospfInstance", + ospf->instance); + } + + ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf); + + if (ospf->new_table == NULL) { + if (!json) + vty_out(vty, "No OSPF routing information exist\n"); + else { + json_object_free(json_router); + if (use_vrf) + json_object_free(json_vrf); + } + return CMD_SUCCESS; + } + + /* Show Network routes. + show_ip_ospf_route_network (vty, ospf->new_table); */ + + /* Show Router routes. */ + show_ip_ospf_route_router(vty, ospf, ospf->new_rtrs, json_router); + + if (json) { + json_object_object_add(json_vrf, "routers", json_router); + if (use_vrf) { + if (ospf->vrf_id == VRF_DEFAULT) + json_object_object_add(json, "default", + json_vrf); + else + json_object_object_add(json, ospf->name, + json_vrf); + } + } else { + vty_out(vty, "\n"); + } + + return CMD_SUCCESS; +} + +DEFPY (show_ip_ospf_border_routers, + show_ip_ospf_border_routers_cmd, + "show ip ospf [vrf ] border-routers [json]", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "All VRFs\n" + "Show all the ABR's and ASBR's\n" + JSON_STR) +{ + struct ospf *ospf = NULL; + struct listnode *node = NULL; + char *vrf_name = NULL; + bool all_vrf = false; + int ret = CMD_SUCCESS; + int inst = 0; + int idx_vrf = 0; + uint8_t use_vrf = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; + + if (uj) + json = json_object_new_object(); + + OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + if (vrf_name) { + bool ospf_output = false; + + use_vrf = 1; + + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + + ospf_output = true; + ret = show_ip_ospf_border_routers_common( + vty, ospf, use_vrf, json); + } + + if (uj) + vty_json(vty, json); + else if (!ospf_output) + vty_out(vty, "%% OSPF is not enabled\n"); + + return ret; + } else { + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + vty_json(vty, json); + else + vty_out(vty, + "%% OSPF is not enabled in vrf %s\n", + vrf_name); + + return CMD_SUCCESS; + } + } + } else { + /* Display default ospf (instance 0) info */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + vty_json(vty, json); + else + vty_out(vty, + "%% OSPF is not enabled in vrf default\n"); + + return CMD_SUCCESS; + } + } + + if (ospf) { + ret = show_ip_ospf_border_routers_common(vty, ospf, use_vrf, + json); + if (uj) + vty_json(vty, json); + } + + return ret; +} + +DEFUN (show_ip_ospf_instance_border_routers, + show_ip_ospf_instance_border_routers_cmd, + "show ip ospf (1-65535) border-routers", + SHOW_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + "Show all the ABR's and ASBR's\n") +{ + int idx_number = 3; + struct ospf *ospf; + unsigned short instance = 0; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + ospf = ospf_lookup_instance(instance); + if (!ospf || !ospf->oi_running) + return CMD_SUCCESS; + + return show_ip_ospf_border_routers_common(vty, ospf, 0, NULL); +} + +static int show_ip_ospf_route_common(struct vty *vty, struct ospf *ospf, + json_object *json, uint8_t use_vrf, + bool detail) +{ + json_object *json_vrf = NULL; + + if (ospf->instance) + vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); + + + if (json) { + if (use_vrf) + json_vrf = json_object_new_object(); + else + json_vrf = json; + } + + ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf); + + if (ospf->new_table == NULL) { + if (json) { + if (use_vrf) + json_object_free(json_vrf); + } else { + vty_out(vty, "No OSPF routing information exist\n"); + } + return CMD_SUCCESS; + } + + if (detail && json == NULL) { + vty_out(vty, "Codes: N - network T - transitive\n"); + vty_out(vty, " IA - inter-area E - external route\n"); + vty_out(vty, " D - destination R - router\n\n"); + } + + /* Show Network routes. */ + show_ip_ospf_route_network(vty, ospf, ospf->new_table, json_vrf, + detail); + + /* Show Router routes. */ + show_ip_ospf_route_router(vty, ospf, ospf->new_rtrs, json_vrf); + + /* Show Router routes. */ + if (ospf->all_rtrs) + show_ip_ospf_route_router(vty, ospf, ospf->all_rtrs, json_vrf); + + /* Show AS External routes. */ + show_ip_ospf_route_external(vty, ospf, ospf->old_external_route, + json_vrf, detail); + + if (json) { + if (use_vrf) { + // json_object_object_add(json_vrf, "areas", + // json_areas); + json_object_object_add(json, ospf_get_name(ospf), + json_vrf); + } + } else { + vty_out(vty, "\n"); + } + + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_route, + show_ip_ospf_route_cmd, + "show ip ospf [vrf ] route [detail] [json]", + SHOW_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "All VRFs\n" + "OSPF routing table\n" + "Detailed information\n" + JSON_STR) +{ + struct ospf *ospf = NULL; + struct listnode *node = NULL; + char *vrf_name = NULL; + bool all_vrf = false; + int ret = CMD_SUCCESS; + int inst = 0; + int idx = 0; + int idx_vrf = 0; + uint8_t use_vrf = 0; + bool uj = use_json(argc, argv); + bool detail = false; + json_object *json = NULL; + + if (uj) + json = json_object_new_object(); + + if (argv_find(argv, argc, "detail", &idx)) + detail = true; + + OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + /* vrf input is provided could be all or specific vrf*/ + if (vrf_name) { + bool ospf_output = false; + + use_vrf = 1; + + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + ospf_output = true; + ret = show_ip_ospf_route_common( + vty, ospf, json, use_vrf, detail); + } + + if (uj) { + /* Keep Non-pretty format */ + vty_json(vty, json); + } else if (!ospf_output) + vty_out(vty, "%% OSPF is not enabled\n"); + + return ret; + } + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + vty_json(vty, json); + else + vty_out(vty, + "%% OSPF is not enabled in vrf %s\n", + vrf_name); + + return CMD_SUCCESS; + } + } else { + /* Display default ospf (instance 0) info */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + vty_json(vty, json); + else + vty_out(vty, + "%% OSPF is not enabled in vrf default\n"); + + return CMD_SUCCESS; + } + } + + if (ospf) { + ret = show_ip_ospf_route_common(vty, ospf, json, use_vrf, + detail); + /* Keep Non-pretty format */ + if (uj) + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_NOSLASHESCAPE)); + } + + if (uj) + json_object_free(json); + + return ret; +} + +DEFUN (show_ip_ospf_instance_route, + show_ip_ospf_instance_route_cmd, + "show ip ospf (1-65535) route [detail]", + SHOW_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + "OSPF routing table\n" + "Detailed information\n") +{ + int idx_number = 3; + int idx = 0; + struct ospf *ospf; + unsigned short instance = 0; + bool detail = false; + + if (argv_find(argv, argc, "detail", &idx)) + detail = true; + + instance = strtoul(argv[idx_number]->arg, NULL, 10); + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + + ospf = ospf_lookup_instance(instance); + if (!ospf || !ospf->oi_running) + return CMD_SUCCESS; + + return show_ip_ospf_route_common(vty, ospf, NULL, 0, detail); +} + + +DEFUN (show_ip_ospf_vrfs, + show_ip_ospf_vrfs_cmd, + "show ip ospf vrfs [json]", + SHOW_STR + IP_STR + "OSPF information\n" + "Show OSPF VRFs \n" + JSON_STR) +{ + bool uj = use_json(argc, argv); + json_object *json = NULL; + json_object *json_vrfs = NULL; + struct ospf *ospf = NULL; + struct listnode *node = NULL; + int count = 0; + static const char header[] = "Name Id RouterId "; + + if (uj) { + json = json_object_new_object(); + json_vrfs = json_object_new_object(); + } + + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + json_object *json_vrf = NULL; + const char *name = NULL; + int64_t vrf_id_ui = 0; + + count++; + + if (!uj && count == 1) + vty_out(vty, "%s\n", header); + if (uj) + json_vrf = json_object_new_object(); + + name = ospf_get_name(ospf); + + vrf_id_ui = (ospf->vrf_id == VRF_UNKNOWN) + ? -1 + : (int64_t)ospf->vrf_id; + + if (uj) { + json_object_int_add(json_vrf, "vrfId", vrf_id_ui); + json_object_string_addf(json_vrf, "routerId", "%pI4", + &ospf->router_id); + + json_object_object_add(json_vrfs, name, json_vrf); + + } else { + vty_out(vty, "%-25s %-5d %-16pI4 \n", name, + ospf->vrf_id, &ospf->router_id); + } + } + + if (uj) { + json_object_object_add(json, "vrfs", json_vrfs); + json_object_int_add(json, "totalVrfs", count); + + vty_json(vty, json); + } else { + if (count) + vty_out(vty, "\nTotal number of OSPF VRFs: %d\n", + count); + } + + return CMD_SUCCESS; +} +DEFPY (clear_ip_ospf_neighbor, + clear_ip_ospf_neighbor_cmd, + "clear ip ospf [(1-65535)]$instance neighbor [A.B.C.D$nbr_id]", + CLEAR_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + "Reset OSPF Neighbor\n" + "Neighbor ID\n") +{ + struct listnode *node; + struct ospf *ospf = NULL; + + /* If user does not specify the arguments, + * instance = 0 and nbr_id = 0.0.0.0 + */ + if (instance != 0) { + /* This means clear only the particular ospf process */ + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + } + + /* Clear all the ospf processes */ + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + + if (nbr_id_str && IPV4_ADDR_SAME(&ospf->router_id, &nbr_id)) { + vty_out(vty, "Self router-id is not allowed.\r\n "); + return CMD_SUCCESS; + } + + ospf_neighbor_reset(ospf, nbr_id, nbr_id_str); + } + + return CMD_SUCCESS; +} + +DEFPY (clear_ip_ospf_process, + clear_ip_ospf_process_cmd, + "clear ip ospf [(1-65535)]$instance process", + CLEAR_STR + IP_STR + "OSPF information\n" + "Instance ID\n" + "Reset OSPF Process\n") +{ + struct listnode *node; + struct ospf *ospf = NULL; + + /* Check if instance is not passed as an argument */ + if (instance != 0) { + /* This means clear only the particular ospf process */ + if (instance != ospf_instance) + return CMD_NOT_MY_INSTANCE; + } + + /* Clear all the ospf processes */ + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + + ospf_process_reset(ospf); + } + + return CMD_SUCCESS; +} + +static const char *const ospf_abr_type_str[] = { + "unknown", "standard", "ibm", "cisco", "shortcut" +}; + +static const char *const ospf_shortcut_mode_str[] = { + "default", "enable", "disable" +}; +static int ospf_vty_external_rt_walkcb(struct hash_bucket *bucket, + void *arg) +{ + struct external_info *ei = bucket->data; + struct vty *vty = (struct vty *)arg; + static unsigned int count; + + vty_out(vty, "%-4pI4/%d, ", &ei->p.prefix, ei->p.prefixlen); + count++; + + if (count % 5 == 0) + vty_out(vty, "\n"); + + if (OSPF_EXTERNAL_RT_COUNT(ei->aggr_route) == count) + count = 0; + + return HASHWALK_CONTINUE; +} + +static int ospf_json_external_rt_walkcb(struct hash_bucket *bucket, + void *arg) +{ + struct external_info *ei = bucket->data; + struct json_object *json = (struct json_object *)arg; + char buf[PREFIX2STR_BUFFER]; + char exnalbuf[20]; + static unsigned int count; + + prefix2str(&ei->p, buf, sizeof(buf)); + + snprintf(exnalbuf, 20, "Exnl Addr-%d", count); + + json_object_string_add(json, exnalbuf, buf); + + count++; + + if (OSPF_EXTERNAL_RT_COUNT(ei->aggr_route) == count) + count = 0; + + return HASHWALK_CONTINUE; +} + +static int ospf_show_summary_address(struct vty *vty, struct ospf *ospf, + uint8_t use_vrf, json_object *json, + bool uj, bool detail) +{ + struct route_node *rn; + json_object *json_vrf = NULL; + int mtype = 0; + int mval = 0; + static char header[] = + "Summary-address Metric-type Metric Tag External_Rt_count\n"; + + mtype = metric_type(ospf, 0, ospf->instance); + mval = metric_value(ospf, 0, ospf->instance); + + if (!uj) + vty_out(vty, "%s\n", header); + + if (uj) { + if (use_vrf) + json_vrf = json_object_new_object(); + else + json_vrf = json; + } + + if (ospf->instance) { + if (uj) + json_object_int_add(json, "ospfInstance", + ospf->instance); + else + vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); + } + + ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf); + + if (!uj) { + vty_out(vty, "aggregation delay interval: %u(in seconds)\n\n", + ospf->aggr_delay_interval); + } else { + json_object_int_add(json_vrf, "aggregationDelayInterval", + ospf->aggr_delay_interval); + } + + for (rn = route_top(ospf->rt_aggr_tbl); rn; rn = route_next(rn)) + if (rn->info) { + struct ospf_external_aggr_rt *aggr = rn->info; + json_object *json_aggr = NULL; + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&aggr->p, buf, sizeof(buf)); + + if (uj) { + + json_aggr = json_object_new_object(); + + json_object_object_add(json_vrf, buf, + json_aggr); + json_object_string_add(json_aggr, + "summaryAddress", buf); + json_object_string_add( + json_aggr, "metricType", + (mtype == EXTERNAL_METRIC_TYPE_1) + ? "E1" + : "E2"); + + json_object_int_add(json_aggr, "metric", mval); + json_object_int_add(json_aggr, "tag", + aggr->tag); + json_object_int_add( + json_aggr, "externalRouteCount", + OSPF_EXTERNAL_RT_COUNT(aggr)); + + if (OSPF_EXTERNAL_RT_COUNT(aggr) && detail) { + hash_walk( + aggr->match_extnl_hash, + ospf_json_external_rt_walkcb, + json_aggr); + } + + } else { + vty_out(vty, "%-20s", buf); + + (mtype == EXTERNAL_METRIC_TYPE_1) + ? vty_out(vty, "%-16s", "E1") + : vty_out(vty, "%-16s", "E2"); + vty_out(vty, "%-11d", mval); + + vty_out(vty, "%-12u", aggr->tag); + + vty_out(vty, "%-5ld\n", + OSPF_EXTERNAL_RT_COUNT(aggr)); + + if (OSPF_EXTERNAL_RT_COUNT(aggr) && detail) { + vty_out(vty, + "Matched External routes:\n"); + hash_walk( + aggr->match_extnl_hash, + ospf_vty_external_rt_walkcb, + vty); + vty_out(vty, "\n"); + } + + vty_out(vty, "\n"); + } + } + + if (uj) { + if (use_vrf) + json_object_object_add(json, ospf_get_name(ospf), + json_vrf); + } else + vty_out(vty, "\n"); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_external_aggregator, + show_ip_ospf_external_aggregator_cmd, + "show ip ospf [vrf ] summary-address [detail] [json]", + SHOW_STR IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "All VRFs\n" + "Show external summary addresses\n" + "Detailed information\n" + JSON_STR) +{ + char *vrf_name = NULL; + bool all_vrf = false; + int ret = CMD_SUCCESS; + int idx_vrf = 0; + int idx = 0; + uint8_t use_vrf = 0; + bool uj = use_json(argc, argv); + struct ospf *ospf = NULL; + json_object *json = NULL; + struct listnode *node = NULL; + int inst = 0; + bool detail = false; + + OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + if (argv_find(argv, argc, "detail", &idx)) + detail = true; + + if (uj) + json = json_object_new_object(); + + /* vrf input is provided */ + if (vrf_name) { + use_vrf = 1; + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + ret = ospf_show_summary_address( + vty, ospf, use_vrf, json, uj, detail); + } + + if (uj) + vty_json(vty, json); + + return ret; + } + + ospf = ospf_lookup_by_inst_name(inst, vrf_name); + + if (ospf == NULL || !ospf->oi_running) { + if (uj) + vty_json(vty, json); + else + vty_out(vty, + "%% OSPF is not enabled in vrf %s\n", + vrf_name); + + return CMD_SUCCESS; + } + ospf_show_summary_address(vty, ospf, use_vrf, json, uj, detail); + + } else { + /* Default Vrf */ + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (ospf == NULL || !ospf->oi_running) { + if (uj) + vty_json(vty, json); + else + vty_out(vty, + "%% OSPF is not enabled in vrf default\n"); + + return CMD_SUCCESS; + } + + ospf_show_summary_address(vty, ospf, use_vrf, json, uj, detail); + } + + if (uj) + vty_json(vty, json); + return CMD_SUCCESS; +} + +static const char *const ospf_int_type_str[] = { + "unknown", /* should never be used. */ + "point-to-point", + "broadcast", + "non-broadcast", + "point-to-multipoint", + "virtual-link", /* should never be used. */ + "loopback" +}; + +static int interface_config_auth_str(struct ospf_if_params *params, char *buf) +{ + if (!OSPF_IF_PARAM_CONFIGURED(params, auth_type) + || params->auth_type == OSPF_AUTH_NOTSET) + return 0; + + /* Translation tables are not that much help + * here due to syntax + * of the simple option */ + switch (params->auth_type) { + + case OSPF_AUTH_NULL: + snprintf(buf, BUFSIZ, " null"); + break; + + case OSPF_AUTH_SIMPLE: + snprintf(buf, BUFSIZ, " "); + break; + + case OSPF_AUTH_CRYPTOGRAPHIC: + if (OSPF_IF_PARAM_CONFIGURED(params, keychain_name)) + snprintf(buf, BUFSIZ, " key-chain %s", params->keychain_name); + else + snprintf(buf, BUFSIZ, " message-digest"); + break; + } + + return 1; +} + +static int config_write_interface_one(struct vty *vty, struct vrf *vrf) +{ + struct listnode *node; + struct interface *ifp; + struct crypt_key *ck; + struct route_node *rn = NULL; + struct ospf_if_params *params; + char buf[BUFSIZ]; + int ret = 0; + int write = 0; + + FOR_ALL_INTERFACES (vrf, ifp) { + + if (memcmp(ifp->name, "VLINK", 5) == 0) + continue; + + if_vty_config_start(vty, ifp); + + if (ifp->desc) + vty_out(vty, " description %s\n", ifp->desc); + + write++; + + params = IF_DEF_PARAMS(ifp); + + do { + /* Interface Network print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, type) + && params->type != OSPF_IFTYPE_LOOPBACK) { + if (params->type != ospf_default_iftype(ifp)) { + vty_out(vty, " ip ospf network %s", + ospf_int_type_str + [params->type]); + if (params->type + == OSPF_IFTYPE_POINTOPOINT + && params->ptp_dmvpn) + vty_out(vty, " dmvpn"); + if (params->type == + OSPF_IFTYPE_POINTOMULTIPOINT && + params->p2mp_delay_reflood) + vty_out(vty, " delay-reflood"); + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", + &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + } + + /* OSPF interface authentication print */ + ret = interface_config_auth_str(params, buf); + if (ret) { + vty_out(vty, " ip ospf authentication%s", + buf); + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", + &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + + /* Simple Authentication Password print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, auth_simple) + && params->auth_simple[0] != '\0') { + vty_out(vty, " ip ospf authentication-key %s", + params->auth_simple); + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", + &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + + /* Cryptographic Authentication Key print. */ + if (params && params->auth_crypt) { + for (ALL_LIST_ELEMENTS_RO(params->auth_crypt, + node, ck)) { + vty_out(vty, + " ip ospf message-digest-key %d md5 %s", + ck->key_id, ck->auth_key); + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", + &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + } + + /* Interface Output Cost print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, output_cost_cmd)) { + vty_out(vty, " ip ospf cost %u", + params->output_cost_cmd); + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", + &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + + /* Hello Interval print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, v_hello) + && params->v_hello != OSPF_HELLO_INTERVAL_DEFAULT) { + vty_out(vty, " ip ospf hello-interval %u", + params->v_hello); + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", + &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + + + /* Router Dead Interval print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, v_wait) + && params->is_v_wait_set) { + vty_out(vty, " ip ospf dead-interval "); + + /* fast hello ? */ + if (OSPF_IF_PARAM_CONFIGURED(params, + fast_hello)) + vty_out(vty, + "minimal hello-multiplier %d", + params->fast_hello); + else + vty_out(vty, "%u", params->v_wait); + + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", + &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + + /* Hello Graceful-Restart Delay print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, + v_gr_hello_delay) && + params->v_gr_hello_delay != + OSPF_HELLO_DELAY_DEFAULT) + vty_out(vty, + " ip ospf graceful-restart hello-delay %u\n", + params->v_gr_hello_delay); + + /* Router Priority print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, priority) + && params->priority + != OSPF_ROUTER_PRIORITY_DEFAULT) { + vty_out(vty, " ip ospf priority %u", + params->priority); + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", + &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + + /* Retransmit Interval print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, + retransmit_interval) + && params->retransmit_interval + != OSPF_RETRANSMIT_INTERVAL_DEFAULT) { + vty_out(vty, " ip ospf retransmit-interval %u", + params->retransmit_interval); + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", + &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + + /* Transmit Delay print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, transmit_delay) + && params->transmit_delay + != OSPF_TRANSMIT_DELAY_DEFAULT) { + vty_out(vty, " ip ospf transmit-delay %u", + params->transmit_delay); + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", + &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + + /* Area print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) { + if (ospf_instance) + vty_out(vty, " ip ospf %d", + ospf_instance); + else + vty_out(vty, " ip ospf"); + + char buf[INET_ADDRSTRLEN]; + + area_id2str(buf, sizeof(buf), ¶ms->if_area, + params->if_area_id_fmt); + vty_out(vty, " area %s", buf); + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", + &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + + /* bfd print. */ + if (params && params->bfd_config) + ospf_bfd_write_config(vty, params); + + /* MTU ignore print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, mtu_ignore) + && params->mtu_ignore != OSPF_MTU_IGNORE_DEFAULT) { + if (params->mtu_ignore == 0) + vty_out(vty, " no ip ospf mtu-ignore"); + else + vty_out(vty, " ip ospf mtu-ignore"); + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", + &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + + if (OSPF_IF_PARAM_CONFIGURED(params, + passive_interface)) { + vty_out(vty, " %sip ospf passive", + params->passive_interface + == OSPF_IF_ACTIVE + ? "no " + : ""); + 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); + + /* Capability opaque print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, opaque_capable) && + params->opaque_capable != + OSPF_OPAQUE_CAPABLE_DEFAULT) { + if (params->opaque_capable == false) + vty_out(vty, + " no ip ospf capability opaque"); + else + vty_out(vty, + " ip ospf capability opaque"); + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + + /* prefix-suppression print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, + prefix_suppression) && + params->prefix_suppression != + OSPF_PREFIX_SUPPRESSION_DEFAULT) { + if (params->prefix_suppression == false) + vty_out(vty, + " no ip ospf prefix-suppression"); + else + vty_out(vty, + " ip ospf prefix-suppression"); + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + + while (1) { + if (rn == NULL) + rn = route_top(IF_OIFS_PARAMS(ifp)); + else + rn = route_next(rn); + + if (rn == NULL) + break; + params = rn->info; + if (params != NULL) + break; + } + } while (rn); + + ospf_opaque_config_write_if(vty, ifp); + + if_vty_config_end(vty); + } + + return write; +} + +/* Configuration write function for ospfd. */ +static int config_write_interface(struct vty *vty) +{ + int write = 0; + struct vrf *vrf = NULL; + + /* Display all VRF aware OSPF interface configuration */ + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + write += config_write_interface_one(vty, vrf); + } + + return write; +} + +static int config_write_network_area(struct vty *vty, struct ospf *ospf) +{ + struct route_node *rn; + char buf[INET_ADDRSTRLEN]; + + /* `network area' print. */ + for (rn = route_top(ospf->networks); rn; rn = route_next(rn)) + if (rn->info) { + struct ospf_network *n = rn->info; + + /* Create Area ID string by specified Area ID format. */ + if (n->area_id_fmt == OSPF_AREA_ID_FMT_DOTTEDQUAD) + inet_ntop(AF_INET, &n->area_id, buf, + sizeof(buf)); + else + snprintf(buf, sizeof(buf), "%lu", + (unsigned long int)ntohl( + n->area_id.s_addr)); + + /* Network print. */ + vty_out(vty, " network %pFX area %s\n", &rn->p, buf); + } + + return 0; +} + +static int config_write_ospf_area(struct vty *vty, struct ospf *ospf) +{ + struct listnode *node; + struct ospf_area *area; + char buf[INET_ADDRSTRLEN]; + + /* Area configuration print. */ + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + struct route_node *rn1; + + area_id2str(buf, sizeof(buf), &area->area_id, + area->area_id_fmt); + + if (area->auth_type != OSPF_AUTH_NULL) { + if (area->auth_type == OSPF_AUTH_SIMPLE) + vty_out(vty, " area %s authentication\n", buf); + else + vty_out(vty, + " area %s authentication message-digest\n", + buf); + } + + if (area->shortcut_configured != OSPF_SHORTCUT_DEFAULT) + vty_out(vty, " area %s shortcut %s\n", buf, + ospf_shortcut_mode_str + [area->shortcut_configured]); + + if ((area->external_routing == OSPF_AREA_STUB) + || (area->external_routing == OSPF_AREA_NSSA)) { + if (area->external_routing == OSPF_AREA_STUB) { + vty_out(vty, " area %s stub", buf); + if (area->no_summary) + vty_out(vty, " no-summary\n"); + vty_out(vty, "\n"); + } else if (area->external_routing == OSPF_AREA_NSSA) { + vty_out(vty, " area %s nssa", buf); + + switch (area->NSSATranslatorRole) { + case OSPF_NSSA_ROLE_NEVER: + vty_out(vty, " translate-never"); + break; + case OSPF_NSSA_ROLE_ALWAYS: + vty_out(vty, " translate-always"); + break; + case OSPF_NSSA_ROLE_CANDIDATE: + break; + } + + if (area->nssa_default_originate.enabled) { + vty_out(vty, + " default-information-originate"); + if (area->nssa_default_originate + .metric_value != -1) + vty_out(vty, " metric %d", + area->nssa_default_originate + .metric_value); + if (area->nssa_default_originate + .metric_type != + DEFAULT_METRIC_TYPE) + vty_out(vty, " metric-type 1"); + } + + if (area->no_summary) + vty_out(vty, " no-summary"); + if (area->suppress_fa) + vty_out(vty, " suppress-fa"); + vty_out(vty, "\n"); + + for (rn1 = route_top(area->nssa_ranges); rn1; + rn1 = route_next(rn1)) { + struct ospf_area_range *range; + + range = rn1->info; + if (!range) + continue; + + vty_out(vty, " area %s nssa range %pFX", + buf, &rn1->p); + + if (range->cost_config != + OSPF_AREA_RANGE_COST_UNSPEC) + vty_out(vty, " cost %u", + range->cost_config); + + if (!CHECK_FLAG( + range->flags, + OSPF_AREA_RANGE_ADVERTISE)) + vty_out(vty, " not-advertise"); + + vty_out(vty, "\n"); + } + } + + if (area->default_cost != 1) + vty_out(vty, " area %s default-cost %d\n", buf, + area->default_cost); + } + + for (rn1 = route_top(area->ranges); rn1; rn1 = route_next(rn1)) + if (rn1->info) { + struct ospf_area_range *range = rn1->info; + + vty_out(vty, " area %s range %pFX", buf, + &rn1->p); + + if (range->cost_config + != OSPF_AREA_RANGE_COST_UNSPEC) + vty_out(vty, " cost %d", + range->cost_config); + + if (!CHECK_FLAG(range->flags, + OSPF_AREA_RANGE_ADVERTISE)) + vty_out(vty, " not-advertise"); + + if (CHECK_FLAG(range->flags, + OSPF_AREA_RANGE_SUBSTITUTE)) + vty_out(vty, " substitute %pI4/%d", + &range->subst_addr, + range->subst_masklen); + + vty_out(vty, "\n"); + } + + if (EXPORT_NAME(area)) + vty_out(vty, " area %s export-list %s\n", buf, + EXPORT_NAME(area)); + + if (IMPORT_NAME(area)) + vty_out(vty, " area %s import-list %s\n", buf, + IMPORT_NAME(area)); + + if (PREFIX_NAME_IN(area)) + vty_out(vty, " area %s filter-list prefix %s in\n", buf, + PREFIX_NAME_IN(area)); + + if (PREFIX_NAME_OUT(area)) + vty_out(vty, " area %s filter-list prefix %s out\n", + buf, PREFIX_NAME_OUT(area)); + + if (area->fr_info.configured) + vty_out(vty, " area %s flood-reduction\n", buf); + } + + return 0; +} + +static int config_write_ospf_nbr_nbma(struct vty *vty, struct ospf *ospf) +{ + struct ospf_nbr_nbma *nbr_nbma; + struct route_node *rn; + + /* Static Neighbor configuration print. */ + for (rn = route_top(ospf->nbr_nbma); rn; rn = route_next(rn)) + if ((nbr_nbma = rn->info)) { + vty_out(vty, " neighbor %pI4", &nbr_nbma->addr); + + if (nbr_nbma->priority + != OSPF_NEIGHBOR_PRIORITY_DEFAULT) + vty_out(vty, " priority %d", + nbr_nbma->priority); + + if (nbr_nbma->v_poll != OSPF_POLL_INTERVAL_DEFAULT) + vty_out(vty, " poll-interval %d", + nbr_nbma->v_poll); + + vty_out(vty, "\n"); + } + + return 0; +} + +static int config_write_virtual_link(struct vty *vty, struct ospf *ospf) +{ + struct listnode *node; + struct ospf_vl_data *vl_data; + char buf[INET_ADDRSTRLEN]; + char buf2[BUFSIZ]; + int ret = 0; + + /* Virtual-Link print */ + for (ALL_LIST_ELEMENTS_RO(ospf->vlinks, node, vl_data)) { + struct listnode *n2; + struct crypt_key *ck; + struct ospf_interface *oi; + + if (vl_data != NULL) { + area_id2str(buf, sizeof(buf), &vl_data->vl_area_id, + vl_data->vl_area_id_fmt); + oi = vl_data->vl_oi; + + /* timers */ + if (OSPF_IF_PARAM(oi, v_hello) + != OSPF_HELLO_INTERVAL_DEFAULT + || OSPF_IF_PARAM(oi, v_wait) + != OSPF_ROUTER_DEAD_INTERVAL_DEFAULT + || OSPF_IF_PARAM(oi, retransmit_interval) + != OSPF_RETRANSMIT_INTERVAL_DEFAULT + || OSPF_IF_PARAM(oi, transmit_delay) + != OSPF_TRANSMIT_DELAY_DEFAULT) + vty_out(vty, + " area %s virtual-link %pI4 hello-interval %d retransmit-interval %d transmit-delay %d dead-interval %d\n", + buf, &vl_data->vl_peer, + OSPF_IF_PARAM(oi, v_hello), + OSPF_IF_PARAM(oi, retransmit_interval), + OSPF_IF_PARAM(oi, transmit_delay), + OSPF_IF_PARAM(oi, v_wait)); + else + vty_out(vty, " area %s virtual-link %pI4\n", buf, + &vl_data->vl_peer); + /* Auth type */ + ret = interface_config_auth_str( + IF_DEF_PARAMS(oi->ifp), buf2); + if (ret) + vty_out(vty, + " area %s virtual-link %pI4 authentication%s\n", + buf, &vl_data->vl_peer, buf2); + /* Auth key */ + if (IF_DEF_PARAMS(vl_data->vl_oi->ifp)->auth_simple[0] + != '\0') + vty_out(vty, + " area %s virtual-link %pI4 authentication-key %s\n", + buf, &vl_data->vl_peer, + IF_DEF_PARAMS(vl_data->vl_oi->ifp) + ->auth_simple); + /* md5 keys */ + for (ALL_LIST_ELEMENTS_RO( + IF_DEF_PARAMS(vl_data->vl_oi->ifp) + ->auth_crypt, + n2, ck)) + vty_out(vty, + " area %s virtual-link %pI4 message-digest-key %d md5 %s\n", + buf, &vl_data->vl_peer, + ck->key_id, ck->auth_key); + } + } + + return 0; +} + + +static int config_write_ospf_redistribute(struct vty *vty, struct ospf *ospf) +{ + int type; + + /* redistribute print. */ + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + struct list *red_list; + struct listnode *node; + struct ospf_redist *red; + + red_list = ospf->redist[type]; + if (!red_list) + continue; + + for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) { + vty_out(vty, " redistribute %s", + zebra_route_string(type)); + if (red->instance) + vty_out(vty, " %d", red->instance); + + if (red->dmetric.value >= 0) + vty_out(vty, " metric %d", red->dmetric.value); + + if (red->dmetric.type == EXTERNAL_METRIC_TYPE_1) + vty_out(vty, " metric-type 1"); + + if (ROUTEMAP_NAME(red)) + vty_out(vty, " route-map %s", + ROUTEMAP_NAME(red)); + + vty_out(vty, "\n"); + } + } + + return 0; +} + +static int ospf_cfg_write_helper_dis_rtr_walkcb(struct hash_bucket *bucket, + void *arg) +{ + struct advRtr *rtr = bucket->data; + struct vty *vty = (struct vty *)arg; + + vty_out(vty, " graceful-restart helper enable %pI4\n", + &rtr->advRtrAddr); + return HASHWALK_CONTINUE; +} + +static void config_write_ospf_gr(struct vty *vty, struct ospf *ospf) +{ + if (!ospf->gr_info.restart_support) + return; + + if (ospf->gr_info.grace_period == OSPF_DFLT_GRACE_INTERVAL) + vty_out(vty, " graceful-restart\n"); + else + vty_out(vty, " graceful-restart grace-period %u\n", + ospf->gr_info.grace_period); +} + +static int config_write_ospf_gr_helper(struct vty *vty, struct ospf *ospf) +{ + if (ospf->is_helper_supported) + vty_out(vty, " graceful-restart helper enable\n"); + + if (!ospf->strict_lsa_check) + vty_out(vty, + " no graceful-restart helper strict-lsa-checking\n"); + + if (ospf->only_planned_restart) + vty_out(vty, " graceful-restart helper planned-only\n"); + + if (ospf->supported_grace_time != OSPF_MAX_GRACE_INTERVAL) + vty_out(vty, + " graceful-restart helper supported-grace-time %d\n", + ospf->supported_grace_time); + + if (OSPF_HELPER_ENABLE_RTR_COUNT(ospf)) { + hash_walk(ospf->enable_rtr_list, + ospf_cfg_write_helper_dis_rtr_walkcb, vty); + } + return 0; +} + +static int config_write_ospf_external_aggregator(struct vty *vty, + struct ospf *ospf) +{ + struct route_node *rn; + + if (ospf->aggr_delay_interval != OSPF_EXTL_AGGR_DEFAULT_DELAY) + vty_out(vty, " aggregation timer %u\n", + ospf->aggr_delay_interval); + + /* print 'summary-address A.B.C.D/M' */ + for (rn = route_top(ospf->rt_aggr_tbl); rn; rn = route_next(rn)) + if (rn->info) { + struct ospf_external_aggr_rt *aggr = rn->info; + + vty_out(vty, " summary-address %pI4/%d", + &aggr->p.prefix, aggr->p.prefixlen); + if (aggr->tag) + vty_out(vty, " tag %u", aggr->tag); + + if (CHECK_FLAG(aggr->flags, + OSPF_EXTERNAL_AGGRT_NO_ADVERTISE)) + vty_out(vty, " no-advertise"); + + vty_out(vty, "\n"); + } + + return 0; +} + +static int config_write_ospf_default_metric(struct vty *vty, struct ospf *ospf) +{ + if (ospf->default_metric != -1) + vty_out(vty, " default-metric %d\n", ospf->default_metric); + return 0; +} + +static int config_write_ospf_distribute(struct vty *vty, struct ospf *ospf) +{ + int type; + struct ospf_redist *red; + + if (ospf) { + /* distribute-list print. */ + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) + if (DISTRIBUTE_NAME(ospf, type)) + vty_out(vty, " distribute-list %s out %s\n", + DISTRIBUTE_NAME(ospf, type), + zebra_route_string(type)); + + /* default-information print. */ + if (ospf->default_originate != DEFAULT_ORIGINATE_NONE) { + vty_out(vty, " default-information originate"); + if (ospf->default_originate == DEFAULT_ORIGINATE_ALWAYS) + vty_out(vty, " always"); + + red = ospf_redist_lookup(ospf, DEFAULT_ROUTE, 0); + if (red) { + if (red->dmetric.value >= 0) + vty_out(vty, " metric %d", + red->dmetric.value); + + if (red->dmetric.type == EXTERNAL_METRIC_TYPE_1) + vty_out(vty, " metric-type 1"); + + if (ROUTEMAP_NAME(red)) + vty_out(vty, " route-map %s", + ROUTEMAP_NAME(red)); + } + + vty_out(vty, "\n"); + } + } + + return 0; +} + +static int config_write_ospf_distance(struct vty *vty, struct ospf *ospf) +{ + struct route_node *rn; + struct ospf_distance *odistance; + + if (ospf->distance_all) + vty_out(vty, " distance %d\n", ospf->distance_all); + + if (ospf->distance_intra || ospf->distance_inter + || ospf->distance_external) { + vty_out(vty, " distance ospf"); + + if (ospf->distance_intra) + vty_out(vty, " intra-area %d", ospf->distance_intra); + if (ospf->distance_inter) + vty_out(vty, " inter-area %d", ospf->distance_inter); + if (ospf->distance_external) + vty_out(vty, " external %d", ospf->distance_external); + + vty_out(vty, "\n"); + } + + for (rn = route_top(ospf->distance_table); rn; rn = route_next(rn)) + if ((odistance = rn->info) != NULL) { + vty_out(vty, " distance %d %pFX %s\n", + odistance->distance, &rn->p, + odistance->access_list ? odistance->access_list + : ""); + } + return 0; +} + +static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) +{ + int write = 0; + + /* `router ospf' print. */ + if (ospf->instance && strcmp(ospf->name, VRF_DEFAULT_NAME)) { + vty_out(vty, "router ospf %d vrf %s\n", ospf->instance, + ospf->name); + } else if (ospf->instance) { + vty_out(vty, "router ospf %d\n", ospf->instance); + } else if (strcmp(ospf->name, VRF_DEFAULT_NAME)) { + vty_out(vty, "router ospf vrf %s\n", ospf->name); + } else + vty_out(vty, "router ospf\n"); + + if (!ospf->networks) { + write++; + return write; + } + + /* Router ID print. */ + if (ospf->router_id_static.s_addr != INADDR_ANY) + vty_out(vty, " ospf router-id %pI4\n", + &ospf->router_id_static); + + /* zebra opaque attributes configuration. */ + if (CHECK_FLAG(ospf->config, OSPF_SEND_EXTRA_DATA_TO_ZEBRA)) + vty_out(vty, " ospf send-extra-data zebra\n"); + + /* ABR type print. */ + if (ospf->abr_type != OSPF_ABR_DEFAULT) + vty_out(vty, " ospf abr-type %s\n", + ospf_abr_type_str[ospf->abr_type]); + + /* log-adjacency-changes flag print. */ + if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_CHANGES)) { + if (CHECK_FLAG(ospf->config, OSPF_LOG_ADJACENCY_DETAIL)) + vty_out(vty, " log-adjacency-changes detail\n"); + else if (!SAVE_OSPF_LOG_ADJACENCY_CHANGES) + vty_out(vty, " log-adjacency-changes\n"); + } else if (SAVE_OSPF_LOG_ADJACENCY_CHANGES) { + vty_out(vty, " no log-adjacency-changes\n"); + } + + /* RFC1583 compatibility flag print -- Compatible with CISCO + * 12.1. */ + if (CHECK_FLAG(ospf->config, OSPF_RFC1583_COMPATIBLE)) + vty_out(vty, " compatible rfc1583\n"); + + /* auto-cost reference-bandwidth configuration. */ + if (ospf->ref_bandwidth != OSPF_DEFAULT_REF_BANDWIDTH) { + vty_out(vty, + "! Important: ensure reference bandwidth is consistent across all routers\n"); + vty_out(vty, " auto-cost reference-bandwidth %d\n", + ospf->ref_bandwidth); + } + + /* SPF timers print. */ + if (ospf->spf_delay != OSPF_SPF_DELAY_DEFAULT + || ospf->spf_holdtime != OSPF_SPF_HOLDTIME_DEFAULT + || ospf->spf_max_holdtime != OSPF_SPF_MAX_HOLDTIME_DEFAULT) + vty_out(vty, " timers throttle spf %d %d %d\n", ospf->spf_delay, + ospf->spf_holdtime, ospf->spf_max_holdtime); + + /* LSA timers print. */ + if (ospf->min_ls_interval != OSPF_MIN_LS_INTERVAL) + vty_out(vty, " timers throttle lsa all %d\n", + ospf->min_ls_interval); + if (ospf->min_ls_arrival != OSPF_MIN_LS_ARRIVAL) + vty_out(vty, " timers lsa min-arrival %d\n", + ospf->min_ls_arrival); + + /* Write multiplier print. */ + if (ospf->write_oi_count != OSPF_WRITE_INTERFACE_COUNT_DEFAULT) + vty_out(vty, " ospf write-multiplier %d\n", + ospf->write_oi_count); + + if (ospf->max_multipath != MULTIPATH_NUM) + vty_out(vty, " maximum-paths %d\n", ospf->max_multipath); + + /* Max-metric router-lsa print */ + config_write_stub_router(vty, ospf); + + /* SPF refresh parameters print. */ + if (ospf->lsa_refresh_interval != OSPF_LSA_REFRESH_INTERVAL_DEFAULT) + vty_out(vty, " refresh timer %d\n", ospf->lsa_refresh_interval); + + if (ospf->fr_configured) + vty_out(vty, " flood-reduction\n"); + + if (!ospf->intf_socket_enabled) + vty_out(vty, " no socket-per-interface\n"); + + /* Redistribute information print. */ + config_write_ospf_redistribute(vty, ospf); + + /* Graceful Restart print */ + config_write_ospf_gr(vty, ospf); + config_write_ospf_gr_helper(vty, ospf); + + /* Print external route aggregation. */ + config_write_ospf_external_aggregator(vty, ospf); + + /* passive-interface print. */ + if (ospf->passive_interface_default == OSPF_IF_PASSIVE) + vty_out(vty, " passive-interface default\n"); + + /* proactive-arp print. */ + if (ospf->proactive_arp != OSPF_PROACTIVE_ARP_DEFAULT) { + if (ospf->proactive_arp) + vty_out(vty, " proactive-arp\n"); + else + vty_out(vty, " no proactive-arp\n"); + } + + /* TI-LFA print. */ + if (ospf->ti_lfa_enabled) { + if (ospf->ti_lfa_protection_type == OSPF_TI_LFA_NODE_PROTECTION) + vty_out(vty, " fast-reroute ti-lfa node-protection\n"); + else + vty_out(vty, " fast-reroute ti-lfa\n"); + } + + /* Network area print. */ + config_write_network_area(vty, ospf); + + /* Area config print. */ + config_write_ospf_area(vty, ospf); + + /* static neighbor print. */ + config_write_ospf_nbr_nbma(vty, ospf); + + /* Virtual-Link print. */ + config_write_virtual_link(vty, ospf); + + /* Default metric configuration. */ + config_write_ospf_default_metric(vty, ospf); + + /* Distribute-list and default-information print. */ + config_write_ospf_distribute(vty, ospf); + + /* Distance configuration. */ + config_write_ospf_distance(vty, ospf); + + ospf_opaque_config_write_router(vty, ospf); + + /* LDP-Sync print */ + ospf_ldp_sync_write_config(vty, ospf); + + /* Socket buffer sizes */ + if (ospf->recv_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE) { + if (ospf->send_sock_bufsize == ospf->recv_sock_bufsize) + vty_out(vty, " socket buffer all %u\n", + ospf->recv_sock_bufsize); + else + vty_out(vty, " socket buffer recv %u\n", + ospf->recv_sock_bufsize); + } + + if (ospf->send_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE && + ospf->send_sock_bufsize != ospf->recv_sock_bufsize) + vty_out(vty, " socket buffer send %u\n", + ospf->send_sock_bufsize); + + + vty_out(vty, "exit\n"); + + write++; + return write; +} + +/* OSPF configuration write function. */ +static int ospf_config_write(struct vty *vty) +{ + struct ospf *ospf; + struct listnode *ospf_node = NULL; + int write = 0; + + if (listcount(om->ospf) == 0) + return write; + + for (ALL_LIST_ELEMENTS_RO(om->ospf, ospf_node, ospf)) { + /* VRF Default check if it is running. + * Upon daemon start, there could be default instance + * in absence of 'router ospf'/oi_running is disabled. */ + if (ospf->vrf_id == VRF_DEFAULT && ospf->oi_running) + write += ospf_config_write_one(vty, ospf); + /* For Non-Default VRF simply display the configuration, + * even if it is not oi_running. */ + else if (ospf->vrf_id != VRF_DEFAULT) + write += ospf_config_write_one(vty, ospf); + } + return write; +} + +void ospf_vty_show_init(void) +{ + /* "show ip ospf" commands. */ + install_element(VIEW_NODE, &show_ip_ospf_cmd); + + install_element(VIEW_NODE, &show_ip_ospf_instance_cmd); + + /* "show ip ospf database" commands. */ + install_element(VIEW_NODE, &show_ip_ospf_database_cmd); + + /* "show ip ospf interface" commands. */ + install_element(VIEW_NODE, &show_ip_ospf_interface_cmd); + + install_element(VIEW_NODE, &show_ip_ospf_instance_interface_cmd); + /* "show ip ospf interface traffic */ + install_element(VIEW_NODE, &show_ip_ospf_interface_traffic_cmd); + + /* "show ip ospf neighbor" commands. */ + install_element(VIEW_NODE, &show_ip_ospf_neighbor_int_detail_cmd); + install_element(VIEW_NODE, &show_ip_ospf_neighbor_int_cmd); + install_element(VIEW_NODE, &show_ip_ospf_neighbor_id_cmd); + install_element(VIEW_NODE, &show_ip_ospf_neighbor_detail_all_cmd); + install_element(VIEW_NODE, &show_ip_ospf_neighbor_detail_cmd); + install_element(VIEW_NODE, &show_ip_ospf_neighbor_cmd); + install_element(VIEW_NODE, &show_ip_ospf_neighbor_all_cmd); + + install_element(VIEW_NODE, + &show_ip_ospf_instance_neighbor_int_detail_cmd); + install_element(VIEW_NODE, &show_ip_ospf_instance_neighbor_int_cmd); + install_element(VIEW_NODE, &show_ip_ospf_instance_neighbor_id_cmd); + install_element(VIEW_NODE, + &show_ip_ospf_instance_neighbor_detail_all_cmd); + install_element(VIEW_NODE, &show_ip_ospf_instance_neighbor_detail_cmd); + install_element(VIEW_NODE, &show_ip_ospf_instance_neighbor_cmd); + install_element(VIEW_NODE, &show_ip_ospf_instance_neighbor_all_cmd); + + /* "show ip ospf route" commands. */ + install_element(VIEW_NODE, &show_ip_ospf_route_cmd); + install_element(VIEW_NODE, &show_ip_ospf_border_routers_cmd); + install_element(VIEW_NODE, &show_ip_ospf_reachable_routers_cmd); + + install_element(VIEW_NODE, &show_ip_ospf_instance_route_cmd); + install_element(VIEW_NODE, &show_ip_ospf_instance_border_routers_cmd); + install_element(VIEW_NODE, + &show_ip_ospf_instance_reachable_routers_cmd); + + /* "show ip ospf vrfs" commands. */ + install_element(VIEW_NODE, &show_ip_ospf_vrfs_cmd); + + /* "show ip ospf gr-helper details" command */ + install_element(VIEW_NODE, &show_ip_ospf_gr_helper_cmd); + + /* "show ip ospf summary-address" command */ + install_element(VIEW_NODE, &show_ip_ospf_external_aggregator_cmd); +} + +/* Initialization of OSPF interface. */ +static void ospf_vty_if_init(void) +{ + /* Install interface node. */ + if_cmd_init(config_write_interface); + + /* "ip ospf authentication" commands. */ + install_element(INTERFACE_NODE, &ip_ospf_authentication_args_addr_cmd); + install_element(INTERFACE_NODE, &ip_ospf_authentication_addr_cmd); + install_element(INTERFACE_NODE, + &no_ip_ospf_authentication_args_addr_cmd); + install_element(INTERFACE_NODE, &no_ip_ospf_authentication_addr_cmd); + install_element(INTERFACE_NODE, &ip_ospf_authentication_key_addr_cmd); + install_element(INTERFACE_NODE, + &no_ip_ospf_authentication_key_authkey_addr_cmd); + install_element(INTERFACE_NODE, + &no_ospf_authentication_key_authkey_addr_cmd); + + /* "ip ospf message-digest-key" commands. */ + install_element(INTERFACE_NODE, &ip_ospf_message_digest_key_cmd); + install_element(INTERFACE_NODE, &no_ip_ospf_message_digest_key_cmd); + + /* "ip ospf cost" commands. */ + install_element(INTERFACE_NODE, &ip_ospf_cost_cmd); + install_element(INTERFACE_NODE, &no_ip_ospf_cost_cmd); + + /* "ip ospf mtu-ignore" commands. */ + install_element(INTERFACE_NODE, &ip_ospf_mtu_ignore_addr_cmd); + install_element(INTERFACE_NODE, &no_ip_ospf_mtu_ignore_addr_cmd); + + /* "ip ospf dead-interval" commands. */ + install_element(INTERFACE_NODE, &ip_ospf_dead_interval_cmd); + install_element(INTERFACE_NODE, + &ip_ospf_dead_interval_minimal_addr_cmd); + install_element(INTERFACE_NODE, &no_ip_ospf_dead_interval_cmd); + + /* "ip ospf hello-interval" commands. */ + install_element(INTERFACE_NODE, &ip_ospf_hello_interval_cmd); + install_element(INTERFACE_NODE, &no_ip_ospf_hello_interval_cmd); + + /* "ip ospf network" commands. */ + install_element(INTERFACE_NODE, &ip_ospf_network_cmd); + install_element(INTERFACE_NODE, &no_ip_ospf_network_cmd); + + /* "ip ospf priority" commands. */ + install_element(INTERFACE_NODE, &ip_ospf_priority_cmd); + install_element(INTERFACE_NODE, &no_ip_ospf_priority_cmd); + + /* "ip ospf retransmit-interval" commands. */ + install_element(INTERFACE_NODE, &ip_ospf_retransmit_interval_addr_cmd); + install_element(INTERFACE_NODE, + &no_ip_ospf_retransmit_interval_addr_cmd); + + /* "ip ospf transmit-delay" commands. */ + install_element(INTERFACE_NODE, &ip_ospf_transmit_delay_addr_cmd); + install_element(INTERFACE_NODE, &no_ip_ospf_transmit_delay_addr_cmd); + + /* "ip ospf area" commands. */ + 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); + + /* "ip ospf capability opaque" commands. */ + install_element(INTERFACE_NODE, &ip_ospf_capability_opaque_addr_cmd); + + /* "ip ospf prefix-suppression" commands. */ + install_element(INTERFACE_NODE, &ip_ospf_prefix_suppression_addr_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); + install_element(INTERFACE_NODE, &no_ospf_message_digest_key_cmd); + install_element(INTERFACE_NODE, &ospf_dead_interval_cmd); + install_element(INTERFACE_NODE, &no_ospf_dead_interval_cmd); + install_element(INTERFACE_NODE, &ospf_hello_interval_cmd); + install_element(INTERFACE_NODE, &no_ospf_hello_interval_cmd); + install_element(INTERFACE_NODE, &ospf_cost_cmd); + install_element(INTERFACE_NODE, &no_ospf_cost_cmd); + install_element(INTERFACE_NODE, &ospf_network_cmd); + install_element(INTERFACE_NODE, &no_ospf_network_cmd); + install_element(INTERFACE_NODE, &ospf_priority_cmd); + install_element(INTERFACE_NODE, &no_ospf_priority_cmd); + install_element(INTERFACE_NODE, &ospf_retransmit_interval_cmd); + install_element(INTERFACE_NODE, &no_ospf_retransmit_interval_cmd); + install_element(INTERFACE_NODE, &ospf_transmit_delay_cmd); + install_element(INTERFACE_NODE, &no_ospf_transmit_delay_cmd); +} + +static void ospf_vty_zebra_init(void) +{ + install_element(OSPF_NODE, &ospf_redistribute_source_cmd); + install_element(OSPF_NODE, &no_ospf_redistribute_source_cmd); + install_element(OSPF_NODE, &ospf_redistribute_instance_source_cmd); + install_element(OSPF_NODE, &no_ospf_redistribute_instance_source_cmd); + + install_element(OSPF_NODE, &ospf_distribute_list_out_cmd); + install_element(OSPF_NODE, &no_ospf_distribute_list_out_cmd); + + install_element(OSPF_NODE, &ospf_default_information_originate_cmd); + install_element(OSPF_NODE, &no_ospf_default_information_originate_cmd); + + install_element(OSPF_NODE, &ospf_default_metric_cmd); + install_element(OSPF_NODE, &no_ospf_default_metric_cmd); + + install_element(OSPF_NODE, &ospf_distance_cmd); + install_element(OSPF_NODE, &no_ospf_distance_cmd); + install_element(OSPF_NODE, &no_ospf_distance_ospf_cmd); + install_element(OSPF_NODE, &ospf_distance_ospf_cmd); + + /*Ospf garcefull restart helper configurations */ + install_element(OSPF_NODE, &ospf_gr_helper_enable_cmd); + install_element(OSPF_NODE, &no_ospf_gr_helper_enable_cmd); + install_element(OSPF_NODE, &ospf_gr_helper_enable_lsacheck_cmd); + install_element(OSPF_NODE, &no_ospf_gr_helper_enable_lsacheck_cmd); + install_element(OSPF_NODE, &ospf_gr_helper_supported_grace_time_cmd); + install_element(OSPF_NODE, &no_ospf_gr_helper_supported_grace_time_cmd); + install_element(OSPF_NODE, &ospf_gr_helper_planned_only_cmd); + install_element(OSPF_NODE, &no_ospf_gr_helper_planned_only_cmd); + + /* External LSA summarisation config commands.*/ + install_element(OSPF_NODE, &ospf_external_route_aggregation_cmd); + install_element(OSPF_NODE, &no_ospf_external_route_aggregation_cmd); + install_element(OSPF_NODE, + &ospf_external_route_aggregation_no_adrvertise_cmd); + install_element(OSPF_NODE, + &no_ospf_external_route_aggregation_no_adrvertise_cmd); + install_element(OSPF_NODE, &ospf_route_aggregation_timer_cmd); + install_element(OSPF_NODE, &no_ospf_route_aggregation_timer_cmd); +} + +static int ospf_config_write(struct vty *vty); +static struct cmd_node ospf_node = { + .name = "ospf", + .node = OSPF_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-router)# ", + .config_write = ospf_config_write, +}; + +static void ospf_interface_clear(struct interface *ifp) +{ + if (!if_is_operative(ifp)) + return; + + if (IS_DEBUG_OSPF(ism, ISM_EVENTS)) + zlog_debug("ISM[%s]: clear by reset", ifp->name); + + ospf_if_reset(ifp); +} + +DEFUN (clear_ip_ospf_interface, + clear_ip_ospf_interface_cmd, + "clear ip ospf [vrf NAME] interface [IFNAME]", + CLEAR_STR + IP_STR + "OSPF information\n" + VRF_CMD_HELP_STR + "Interface information\n" + "Interface name\n") +{ + int idx_ifname = 0; + int idx_vrf = 0; + struct interface *ifp; + struct listnode *node; + struct ospf *ospf = NULL; + char *vrf_name = NULL; + vrf_id_t vrf_id = VRF_DEFAULT; + struct vrf *vrf = NULL; + + if (argv_find(argv, argc, "vrf", &idx_vrf)) + vrf_name = argv[idx_vrf + 1]->arg; + if (vrf_name && strmatch(vrf_name, VRF_DEFAULT_NAME)) + vrf_name = NULL; + if (vrf_name) { + vrf = vrf_lookup_by_name(vrf_name); + if (vrf) + vrf_id = vrf->vrf_id; + } + if (!argv_find(argv, argc, "IFNAME", &idx_ifname)) { + /* Clear all the ospfv2 interfaces. */ + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (vrf_id != ospf->vrf_id) + continue; + if (!vrf) + vrf = vrf_lookup_by_id(ospf->vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) + ospf_interface_clear(ifp); + } + } else { + /* Interface name is specified. */ + ifp = if_lookup_by_name(argv[idx_ifname]->arg, vrf_id); + if (ifp == NULL) + vty_out(vty, "No such interface name\n"); + else + ospf_interface_clear(ifp); + } + + return CMD_SUCCESS; +} + +DEFPY_HIDDEN(ospf_lsa_refresh_timer, ospf_lsa_refresh_timer_cmd, + "[no$no] ospf lsa-refresh [(120-1800)]$value", + NO_STR OSPF_STR + "OSPF lsa refresh timer\n" + "timer value in seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf) + + if (no) + ospf->lsa_refresh_timer = OSPF_LS_REFRESH_TIME; + else + ospf->lsa_refresh_timer = value; + + return CMD_SUCCESS; +} + +DEFPY_HIDDEN(ospf_maxage_delay_timer, ospf_maxage_delay_timer_cmd, + "[no$no] ospf maxage-delay [(0-60)]$value", + NO_STR OSPF_STR + "OSPF lsa maxage delay timer\n" + "timer value in seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf) + + if (no) + ospf->maxage_delay = OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT; + else + ospf->maxage_delay = value; + + EVENT_OFF(ospf->t_maxage); + OSPF_TIMER_ON(ospf->t_maxage, ospf_maxage_lsa_remover, + ospf->maxage_delay); + + return CMD_SUCCESS; +} + +/* + * ------------------------------------------------------------------------* + * Following is (vty) configuration functions for flood-reduction handling. + * ------------------------------------------------------------------------ + */ + +DEFPY(flood_reduction, flood_reduction_cmd, "flood-reduction", + "flood reduction feature\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf) + struct ospf_area *area; + struct listnode *node; + + /* Turn on the Flood Reduction feature for the router. */ + if (!ospf->fr_configured) { + ospf->fr_configured = true; + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "Flood Reduction: OFF -> ON"); + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (area) { + ospf_area_update_fr_state(area); + ospf_refresh_area_self_lsas(area); + } + } + } + + return CMD_SUCCESS; +} + +DEFPY(flood_reduction_area, flood_reduction_area_cmd, + "area flood-reduction", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Enable flood reduction for area\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf) + struct ospf_area *oa; + int idx = 1; + int format; + int ret; + const char *areaid; + struct in_addr area_id; + + areaid = argv[idx]->arg; + + ret = str2area_id(areaid, &area_id, &format); + if (ret < 0) { + vty_out(vty, "Please specify area by A.B.C.D|<0-4294967295>\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + oa = ospf_area_lookup_by_area_id(ospf, area_id); + if (!oa) { + vty_out(vty, "OSPF area ID not present\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Turn on the Flood Reduction feature for the area. */ + if (!oa->fr_info.configured) { + oa->fr_info.configured = true; + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "Flood Reduction area %pI4 : OFF -> ON", + &oa->area_id); + ospf_area_update_fr_state(oa); + ospf_refresh_area_self_lsas(oa); + } + + return CMD_SUCCESS; +} + +DEFPY(no_flood_reduction, no_flood_reduction_cmd, "no flood-reduction", + NO_STR "flood reduction feature\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf) + struct listnode *node; + struct ospf_area *area; + + /* Turn off the Flood Reduction feature for the router. */ + if (ospf->fr_configured) { + ospf->fr_configured = false; + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "Flood Reduction: ON -> OFF"); + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (area) { + ospf_area_update_fr_state(area); + ospf_refresh_area_self_lsas(area); + } + } + } + + return CMD_SUCCESS; +} + +DEFPY(no_flood_reduction_area, no_flood_reduction_area_cmd, + "no area flood-reduction", + NO_STR + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Disable flood reduction for area\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf) + struct ospf_area *oa; + int idx = 2; + int format; + int ret; + const char *areaid; + struct in_addr area_id; + + areaid = argv[idx]->arg; + + ret = str2area_id(areaid, &area_id, &format); + if (ret < 0) { + vty_out(vty, "Please specify area by A.B.C.D|<0-4294967295>\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + oa = ospf_area_lookup_by_area_id(ospf, area_id); + if (!oa) { + vty_out(vty, "OSPF area ID not present\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Turn off the Flood Reduction feature for the area. */ + if (oa->fr_info.configured) { + oa->fr_info.configured = false; + OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, + "Flood Reduction area %pI4 : ON -> OFF", + &oa->area_id); + ospf_area_update_fr_state(oa); + ospf_refresh_area_self_lsas(oa); + } + + return CMD_SUCCESS; +} + +DEFPY(ospf_socket_bufsizes, + ospf_socket_bufsizes_cmd, + "[no] socket buffer \ + ![(1-4000000000)$bufsize]", + NO_STR + "Socket parameters\n" + "Buffer size configuration\n" + "Send buffer size\n" + "Receive buffer size\n" + "Both send and receive buffer sizes\n" + "Buffer size, in bytes\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + uint32_t recvsz, sendsz; + + if (no) + bufsize = OSPF_DEFAULT_SOCK_BUFSIZE; + + if (all_val) { + recvsz = bufsize; + sendsz = bufsize; + } else if (send_val) { + sendsz = bufsize; + recvsz = ospf->recv_sock_bufsize; + } else if (recv_val) { + recvsz = bufsize; + sendsz = ospf->send_sock_bufsize; + } else + return CMD_SUCCESS; + + /* React to a change by modifying existing sockets */ + ospf_update_bufsize(ospf, recvsz, sendsz); + + return CMD_SUCCESS; +} + +DEFPY (per_intf_socket, + per_intf_socket_cmd, + "[no] socket-per-interface", + NO_STR + "Use write socket per interface\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct listnode *node; + struct ospf_interface *oi; + + if (no) { + if (ospf->intf_socket_enabled) { + ospf->intf_socket_enabled = false; + + /* Iterate and close any sockets */ + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) + ospf_ifp_sock_close(oi->ifp); + } + } else if (!ospf->intf_socket_enabled) { + ospf->intf_socket_enabled = true; + + /* Iterate and open sockets */ + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) + ospf_ifp_sock_init(oi->ifp); + } + + return CMD_SUCCESS; +} + +void ospf_vty_clear_init(void) +{ + install_element(ENABLE_NODE, &clear_ip_ospf_interface_cmd); + install_element(ENABLE_NODE, &clear_ip_ospf_process_cmd); + install_element(ENABLE_NODE, &clear_ip_ospf_neighbor_cmd); +} + + +/* Install OSPF related vty commands. */ +void ospf_vty_init(void) +{ + /* Install ospf top node. */ + install_node(&ospf_node); + + /* "router ospf" commands. */ + install_element(CONFIG_NODE, &router_ospf_cmd); + install_element(CONFIG_NODE, &no_router_ospf_cmd); + + + install_default(OSPF_NODE); + + /* "ospf router-id" commands. */ + install_element(OSPF_NODE, &ospf_router_id_cmd); + install_element(OSPF_NODE, &ospf_router_id_old_cmd); + 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. */ + install_element(OSPF_NODE, &ospf_abr_type_cmd); + install_element(OSPF_NODE, &no_ospf_abr_type_cmd); + + /* "ospf log-adjacency-changes" commands. */ + install_element(OSPF_NODE, &ospf_log_adjacency_changes_cmd); + install_element(OSPF_NODE, &ospf_log_adjacency_changes_detail_cmd); + install_element(OSPF_NODE, &no_ospf_log_adjacency_changes_cmd); + install_element(OSPF_NODE, &no_ospf_log_adjacency_changes_detail_cmd); + + /* "ospf rfc1583-compatible" commands. */ + install_element(OSPF_NODE, &ospf_compatible_rfc1583_cmd); + install_element(OSPF_NODE, &no_ospf_compatible_rfc1583_cmd); + install_element(OSPF_NODE, &ospf_rfc1583_flag_cmd); + install_element(OSPF_NODE, &no_ospf_rfc1583_flag_cmd); + + /* "ospf send-extra-data zebra" commands. */ + install_element(OSPF_NODE, &ospf_send_extra_data_cmd); + + /* "network area" commands. */ + install_element(OSPF_NODE, &ospf_network_area_cmd); + install_element(OSPF_NODE, &no_ospf_network_area_cmd); + + /* "area authentication" commands. */ + install_element(OSPF_NODE, + &ospf_area_authentication_message_digest_cmd); + install_element(OSPF_NODE, &ospf_area_authentication_cmd); + install_element(OSPF_NODE, &no_ospf_area_authentication_cmd); + + /* "area range" commands. */ + install_element(OSPF_NODE, &ospf_area_range_cmd); + install_element(OSPF_NODE, &ospf_area_range_cost_cmd); + install_element(OSPF_NODE, &ospf_area_range_not_advertise_cmd); + install_element(OSPF_NODE, &no_ospf_area_range_cmd); + install_element(OSPF_NODE, &no_ospf_area_range_substitute_cmd); + + /* "area virtual-link" commands. */ + install_element(OSPF_NODE, &ospf_area_vlink_cmd); + install_element(OSPF_NODE, &ospf_area_vlink_intervals_cmd); + install_element(OSPF_NODE, &no_ospf_area_vlink_cmd); + install_element(OSPF_NODE, &no_ospf_area_vlink_intervals_cmd); + + + /* "area stub" commands. */ + install_element(OSPF_NODE, &ospf_area_stub_no_summary_cmd); + install_element(OSPF_NODE, &ospf_area_stub_cmd); + install_element(OSPF_NODE, &no_ospf_area_stub_no_summary_cmd); + install_element(OSPF_NODE, &no_ospf_area_stub_cmd); + + /* "area nssa" commands. */ + install_element(OSPF_NODE, &ospf_area_nssa_cmd); + install_element(OSPF_NODE, &no_ospf_area_nssa_cmd); + install_element(OSPF_NODE, &ospf_area_nssa_range_cmd); + install_element(OSPF_NODE, &no_ospf_area_nssa_range_cmd); + + install_element(OSPF_NODE, &ospf_area_default_cost_cmd); + install_element(OSPF_NODE, &no_ospf_area_default_cost_cmd); + + install_element(OSPF_NODE, &ospf_area_shortcut_cmd); + install_element(OSPF_NODE, &no_ospf_area_shortcut_cmd); + + install_element(OSPF_NODE, &ospf_area_export_list_cmd); + install_element(OSPF_NODE, &no_ospf_area_export_list_cmd); + + install_element(OSPF_NODE, &ospf_area_filter_list_cmd); + install_element(OSPF_NODE, &no_ospf_area_filter_list_cmd); + + install_element(OSPF_NODE, &ospf_area_import_list_cmd); + install_element(OSPF_NODE, &no_ospf_area_import_list_cmd); + + /* SPF timer commands */ + install_element(OSPF_NODE, &ospf_timers_throttle_spf_cmd); + install_element(OSPF_NODE, &no_ospf_timers_throttle_spf_cmd); + + /* LSA timers commands */ + install_element(OSPF_NODE, &ospf_timers_min_ls_interval_cmd); + install_element(OSPF_NODE, &no_ospf_timers_min_ls_interval_cmd); + install_element(OSPF_NODE, &ospf_timers_lsa_min_arrival_cmd); + install_element(OSPF_NODE, &no_ospf_timers_lsa_min_arrival_cmd); + + /* refresh timer commands */ + install_element(OSPF_NODE, &ospf_refresh_timer_cmd); + install_element(OSPF_NODE, &no_ospf_refresh_timer_val_cmd); + + /* max-metric commands */ + install_element(OSPF_NODE, &ospf_max_metric_router_lsa_admin_cmd); + install_element(OSPF_NODE, &no_ospf_max_metric_router_lsa_admin_cmd); + install_element(OSPF_NODE, &ospf_max_metric_router_lsa_startup_cmd); + install_element(OSPF_NODE, &no_ospf_max_metric_router_lsa_startup_cmd); + install_element(OSPF_NODE, &ospf_max_metric_router_lsa_shutdown_cmd); + install_element(OSPF_NODE, &no_ospf_max_metric_router_lsa_shutdown_cmd); + + /* reference bandwidth commands */ + install_element(OSPF_NODE, &ospf_auto_cost_reference_bandwidth_cmd); + install_element(OSPF_NODE, &no_ospf_auto_cost_reference_bandwidth_cmd); + + /* "neighbor" commands. */ + install_element(OSPF_NODE, &ospf_neighbor_cmd); + install_element(OSPF_NODE, &ospf_neighbor_poll_interval_cmd); + install_element(OSPF_NODE, &no_ospf_neighbor_cmd); + install_element(OSPF_NODE, &no_ospf_neighbor_poll_cmd); + + /* write multiplier commands */ + install_element(OSPF_NODE, &ospf_write_multiplier_cmd); + install_element(OSPF_NODE, &write_multiplier_cmd); + install_element(OSPF_NODE, &no_ospf_write_multiplier_cmd); + install_element(OSPF_NODE, &no_write_multiplier_cmd); + + /* "proactive-arp" commands. */ + install_element(OSPF_NODE, &ospf_proactive_arp_cmd); + install_element(OSPF_NODE, &no_ospf_proactive_arp_cmd); + + /* TI-LFA commands */ + install_element(OSPF_NODE, &ospf_ti_lfa_cmd); + install_element(OSPF_NODE, &no_ospf_ti_lfa_cmd); + + /* Max path configurations */ + install_element(OSPF_NODE, &ospf_max_multipath_cmd); + install_element(OSPF_NODE, &no_ospf_max_multipath_cmd); + + vrf_cmd_init(NULL); + + install_element(OSPF_NODE, &ospf_lsa_refresh_timer_cmd); + install_element(OSPF_NODE, &ospf_maxage_delay_timer_cmd); + + /* Flood Reduction commands */ + install_element(OSPF_NODE, &flood_reduction_cmd); + install_element(OSPF_NODE, &no_flood_reduction_cmd); + install_element(OSPF_NODE, &flood_reduction_area_cmd); + install_element(OSPF_NODE, &no_flood_reduction_area_cmd); + + install_element(OSPF_NODE, &ospf_socket_bufsizes_cmd); + install_element(OSPF_NODE, &per_intf_socket_cmd); + + /* Init interface related vty commands. */ + ospf_vty_if_init(); + + /* Init zebra related vty commands. */ + ospf_vty_zebra_init(); +} diff --git a/ospfd/ospf_vty.h b/ospfd/ospf_vty.h new file mode 100644 index 0000000..e940246 --- /dev/null +++ b/ospfd/ospf_vty.h @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* OSPF VTY interface. + * Copyright (C) 2000 Toshiaki Takada + */ + +#ifndef _QUAGGA_OSPF_VTY_H +#define _QUAGGA_OSPF_VTY_H + +/* Macros. */ +#define VTY_GET_OSPF_AREA_ID(V, F, STR) \ + { \ + int retv; \ + retv = str2area_id((STR), &(V), &(F)); \ + if (retv < 0) { \ + vty_out(vty, "%% Invalid OSPF area ID\n"); \ + return CMD_WARNING; \ + } \ + } + +#define VTY_GET_OSPF_AREA_ID_NO_BB(NAME, V, F, STR) \ + { \ + int retv; \ + retv = str2area_id((STR), &(V), &(F)); \ + if (retv < 0) { \ + vty_out(vty, "%% Invalid OSPF area ID\n"); \ + return CMD_WARNING; \ + } \ + if (OSPF_IS_AREA_ID_BACKBONE((V))) { \ + vty_out(vty, \ + "%% You can't configure %s to backbone\n", \ + NAME); \ + return CMD_WARNING; \ + } \ + } + +/* Prototypes. */ +extern void ospf_vty_init(void); +extern void ospf_vty_show_init(void); +extern void ospf_vty_clear_init(void); +extern int str2area_id(const char *, struct in_addr *, int *); + +/* unit tests */ +void show_ip_ospf_database_summary(struct vty *vty, struct ospf *ospf, int self, + json_object *json); + +#endif /* _QUAGGA_OSPF_VTY_H */ diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c new file mode 100644 index 0000000..1af703d --- /dev/null +++ b/ospfd/ospf_zebra.c @@ -0,0 +1,2224 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zebra connect library for OSPFd + * Copyright (C) 1997, 98, 99, 2000 Kunihiro Ishiguro, Toshiaki Takada + */ + +#include + +#include "frrevent.h" +#include "command.h" +#include "network.h" +#include "prefix.h" +#include "routemap.h" +#include "table.h" +#include "stream.h" +#include "memory.h" +#include "zclient.h" +#include "filter.h" +#include "plist.h" +#include "log.h" +#include "route_opaque.h" +#include "lib/bfd.h" +#include "lib/lib_errors.h" +#include "nexthop.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_te.h" +#include "ospfd/ospf_sr.h" +#include "ospfd/ospf_ldp_sync.h" + +DEFINE_MTYPE_STATIC(OSPFD, OSPF_EXTERNAL, "OSPF External route table"); +DEFINE_MTYPE_STATIC(OSPFD, OSPF_REDISTRIBUTE, "OSPF Redistriute"); + + +/* Zebra structure to hold current status. */ +struct zclient *zclient = NULL; +/* and for the Synchronous connection to the Label Manager */ +struct zclient *zclient_sync; + +/* For registering threads. */ +extern struct event_loop *master; + +/* Router-id update message from zebra. */ +static int ospf_router_id_update_zebra(ZAPI_CALLBACK_ARGS) +{ + struct ospf *ospf = NULL; + struct prefix router_id; + zebra_router_id_update_read(zclient->ibuf, &router_id); + + if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra rcvd: router id update %pFX vrf %s id %u", + &router_id, ospf_vrf_id_to_name(vrf_id), vrf_id); + + ospf = ospf_lookup_by_vrf_id(vrf_id); + + if (ospf != NULL) { + ospf->router_id_zebra = router_id.u.prefix4; + ospf_router_id_update(ospf); + } else { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: ospf instance not found for vrf %s id %u router_id %pFX", + __func__, ospf_vrf_id_to_name(vrf_id), vrf_id, + &router_id); + } + return 0; +} + +static int ospf_interface_address_add(ZAPI_CALLBACK_ARGS) +{ + struct connected *c; + struct ospf *ospf = NULL; + + + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); + + if (c == NULL) + return 0; + + if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra: interface %s address add %pFX vrf %s id %u", + c->ifp->name, c->address, + ospf_vrf_id_to_name(vrf_id), vrf_id); + + ospf = ospf_lookup_by_vrf_id(vrf_id); + if (!ospf) + return 0; + + ospf_if_update(ospf, c->ifp); + + ospf_if_interface(c->ifp); + + return 0; +} + +static int ospf_interface_address_delete(ZAPI_CALLBACK_ARGS) +{ + struct connected *c; + struct interface *ifp; + struct ospf_interface *oi; + struct route_node *rn; + struct prefix p; + + c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); + + if (c == NULL) + return 0; + + if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra: interface %s address delete %pFX", + c->ifp->name, c->address); + + ifp = c->ifp; + p = *c->address; + p.prefixlen = IPV4_MAX_BITLEN; + + rn = route_node_lookup(IF_OIFS(ifp), &p); + if (!rn) { + connected_free(&c); + return 0; + } + + assert(rn->info); + oi = rn->info; + route_unlock_node(rn); + + /* Call interface hook functions to clean up */ + ospf_if_free(oi); + + ospf_if_interface(c->ifp); + + connected_free(&c); + + return 0; +} + +static int ospf_interface_link_params(ZAPI_CALLBACK_ARGS) +{ + struct interface *ifp; + bool changed = false; + + ifp = zebra_interface_link_params_read(zclient->ibuf, vrf_id, &changed); + + if (ifp == NULL || !changed) + return 0; + + /* Update TE TLV */ + ospf_mpls_te_update_if(ifp); + + return 0; +} + +/* Nexthop, ifindex, distance and metric information. */ +static void ospf_zebra_add_nexthop(struct ospf *ospf, struct ospf_path *path, + struct zapi_route *api) +{ + struct zapi_nexthop *api_nh; + struct zapi_nexthop *api_nh_backup; + + /* TI-LFA backup path label stack comes first, if present */ + if (path->srni.backup_label_stack) { + api_nh_backup = &api->backup_nexthops[api->backup_nexthop_num]; + api_nh_backup->vrf_id = ospf->vrf_id; + + api_nh_backup->type = NEXTHOP_TYPE_IPV4; + api_nh_backup->gate.ipv4 = path->srni.backup_nexthop; + + api_nh_backup->label_num = + path->srni.backup_label_stack->num_labels; + memcpy(api_nh_backup->labels, + path->srni.backup_label_stack->label, + sizeof(mpls_label_t) * api_nh_backup->label_num); + + api->backup_nexthop_num++; + } + + /* And here comes the primary nexthop */ + api_nh = &api->nexthops[api->nexthop_num]; +#ifdef HAVE_NETLINK + if (path->unnumbered + || (path->nexthop.s_addr != INADDR_ANY && path->ifindex != 0)) { +#else /* HAVE_NETLINK */ + if (path->nexthop.s_addr != INADDR_ANY && path->ifindex != 0) { +#endif /* HAVE_NETLINK */ + api_nh->gate.ipv4 = path->nexthop; + api_nh->ifindex = path->ifindex; + api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; + } else if (path->nexthop.s_addr != INADDR_ANY) { + api_nh->gate.ipv4 = path->nexthop; + api_nh->type = NEXTHOP_TYPE_IPV4; + } else { + api_nh->ifindex = path->ifindex; + api_nh->type = NEXTHOP_TYPE_IFINDEX; + } + api_nh->vrf_id = ospf->vrf_id; + + /* Set TI-LFA backup nexthop info if present */ + if (path->srni.backup_label_stack) { + SET_FLAG(api->message, ZAPI_MESSAGE_BACKUP_NEXTHOPS); + SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP); + + /* Just care about a single TI-LFA backup path for now */ + api_nh->backup_num = 1; + api_nh->backup_idx[0] = api->backup_nexthop_num - 1; + } + + api->nexthop_num++; +} + +static void ospf_zebra_append_opaque_attr(struct ospf_route *or, + struct zapi_route *api) +{ + struct ospf_zebra_opaque ospf_opaque = {}; + + /* OSPF path type */ + snprintf(ospf_opaque.path_type, sizeof(ospf_opaque.path_type), "%s", + ospf_path_type_name(or->path_type)); + + switch (or->path_type) { + case OSPF_PATH_INTRA_AREA: + case OSPF_PATH_INTER_AREA: + /* OSPF area ID */ + (void)inet_ntop(AF_INET, &or->u.std.area_id, + ospf_opaque.area_id, + sizeof(ospf_opaque.area_id)); + break; + case OSPF_PATH_TYPE1_EXTERNAL: + case OSPF_PATH_TYPE2_EXTERNAL: + /* OSPF route tag */ + snprintf(ospf_opaque.tag, sizeof(ospf_opaque.tag), "%u", + or->u.ext.tag); + break; + default: + break; + } + + SET_FLAG(api->message, ZAPI_MESSAGE_OPAQUE); + api->opaque.length = sizeof(struct ospf_zebra_opaque); + memcpy(api->opaque.data, &ospf_opaque, api->opaque.length); +} + +void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p, + struct ospf_route * or) +{ + struct zapi_route api; + uint8_t distance; + struct ospf_path *path; + struct listnode *node; + + if (ospf->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "Zebra: Graceful Restart in progress -- not installing %pFX", + p); + return; + } + + memset(&api, 0, sizeof(api)); + api.vrf_id = ospf->vrf_id; + api.type = ZEBRA_ROUTE_OSPF; + api.instance = ospf->instance; + api.safi = SAFI_UNICAST; + + memcpy(&api.prefix, p, sizeof(*p)); + SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); + + /* Metric value. */ + SET_FLAG(api.message, ZAPI_MESSAGE_METRIC); + if (or->path_type == OSPF_PATH_TYPE1_EXTERNAL) + api.metric = or->cost + or->u.ext.type2_cost; + else if (or->path_type == OSPF_PATH_TYPE2_EXTERNAL) + api.metric = or->u.ext.type2_cost; + else + api.metric = or->cost; + + /* Check if path type is ASE */ + if (((or->path_type == OSPF_PATH_TYPE1_EXTERNAL) + || (or->path_type == OSPF_PATH_TYPE2_EXTERNAL)) + && (or->u.ext.tag > 0) && (or->u.ext.tag <= ROUTE_TAG_MAX)) { + SET_FLAG(api.message, ZAPI_MESSAGE_TAG); + api.tag = or->u.ext.tag; + } + + /* Distance value. */ + distance = ospf_distance_apply(ospf, p, or); + if (distance) { + SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); + api.distance = distance; + } + + for (ALL_LIST_ELEMENTS_RO(or->paths, node, path)) { + if (api.nexthop_num >= ospf->max_multipath) + break; + + ospf_zebra_add_nexthop(ospf, path, &api); + + if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) { + struct interface *ifp; + + ifp = if_lookup_by_index(path->ifindex, ospf->vrf_id); + + zlog_debug( + "Zebra: Route add %pFX nexthop %pI4, ifindex=%d %s", + p, &path->nexthop, path->ifindex, + ifp ? ifp->name : " "); + } + } + + if (CHECK_FLAG(ospf->config, OSPF_SEND_EXTRA_DATA_TO_ZEBRA)) + ospf_zebra_append_opaque_attr(or, &api); + + zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); +} + +void ospf_zebra_delete(struct ospf *ospf, struct prefix_ipv4 *p, + struct ospf_route * or) +{ + struct zapi_route api; + + if (ospf->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "Zebra: Graceful Restart in progress -- not uninstalling %pFX", + p); + return; + } + + memset(&api, 0, sizeof(api)); + api.vrf_id = ospf->vrf_id; + api.type = ZEBRA_ROUTE_OSPF; + api.instance = ospf->instance; + api.safi = SAFI_UNICAST; + memcpy(&api.prefix, p, sizeof(*p)); + + if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug("Zebra: Route delete %pFX", p); + + zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); +} + +void ospf_zebra_add_discard(struct ospf *ospf, struct prefix_ipv4 *p) +{ + struct zapi_route api; + + if (ospf->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "Zebra: Graceful Restart in progress -- not installing %pFX", + p); + return; + } + + memset(&api, 0, sizeof(api)); + api.vrf_id = ospf->vrf_id; + api.type = ZEBRA_ROUTE_OSPF; + api.instance = ospf->instance; + api.safi = SAFI_UNICAST; + memcpy(&api.prefix, p, sizeof(*p)); + zapi_route_set_blackhole(&api, BLACKHOLE_NULL); + + zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); + + if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug("Zebra: Route add discard %pFX", p); +} + +void ospf_zebra_delete_discard(struct ospf *ospf, struct prefix_ipv4 *p) +{ + struct zapi_route api; + + if (ospf->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "Zebra: Graceful Restart in progress -- not uninstalling %pFX", + p); + return; + } + + memset(&api, 0, sizeof(api)); + api.vrf_id = ospf->vrf_id; + api.type = ZEBRA_ROUTE_OSPF; + api.instance = ospf->instance; + api.safi = SAFI_UNICAST; + memcpy(&api.prefix, p, sizeof(*p)); + zapi_route_set_blackhole(&api, BLACKHOLE_NULL); + + zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); + + if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug("Zebra: Route delete discard %pFX", p); +} + +struct ospf_external *ospf_external_lookup(struct ospf *ospf, uint8_t type, + unsigned short instance) +{ + struct list *ext_list; + struct listnode *node; + struct ospf_external *ext; + + ext_list = ospf->external[type]; + if (!ext_list) + return (NULL); + + for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) + if (ext->instance == instance) + return ext; + + return NULL; +} + +struct ospf_external *ospf_external_add(struct ospf *ospf, uint8_t type, + unsigned short instance) +{ + struct list *ext_list; + struct ospf_external *ext; + + ext = ospf_external_lookup(ospf, type, instance); + if (ext) + return ext; + + if (!ospf->external[type]) + ospf->external[type] = list_new(); + + ext_list = ospf->external[type]; + ext = XCALLOC(MTYPE_OSPF_EXTERNAL, sizeof(struct ospf_external)); + ext->instance = instance; + EXTERNAL_INFO(ext) = route_table_init(); + + listnode_add(ext_list, ext); + + return ext; +} + +/* + * Walk all the ei received from zebra for a route type and apply + * default route-map. + */ +bool ospf_external_default_routemap_apply_walk(struct ospf *ospf, + struct list *ext_list, + struct external_info *default_ei) +{ + struct listnode *node; + struct ospf_external *ext; + struct route_node *rn; + struct external_info *ei = NULL; + int ret = 0; + + for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) { + if (!ext->external_info) + continue; + + for (rn = route_top(ext->external_info); rn; + rn = route_next(rn)) { + ei = rn->info; + if (!ei) + continue; + ret = ospf_external_info_apply_default_routemap( + ospf, ei, default_ei); + if (ret) + break; + } + } + + if (ret && ei) { + if (IS_DEBUG_OSPF_DEFAULT_INFO) + zlog_debug("Default originate routemap permit ei: %pI4", + &ei->p.prefix); + return true; + } + + return false; +} + +/* + * Function to originate or flush default after applying + * route-map on all ei. + */ +static void ospf_external_lsa_default_routemap_timer(struct event *thread) +{ + struct list *ext_list; + struct ospf *ospf = EVENT_ARG(thread); + struct prefix_ipv4 p; + int type; + int ret = 0; + struct ospf_lsa *lsa; + struct external_info *default_ei; + + p.family = AF_INET; + p.prefixlen = 0; + p.prefix.s_addr = INADDR_ANY; + + /* Get the default extenal info. */ + default_ei = ospf_external_info_lookup(ospf, DEFAULT_ROUTE, + ospf->instance, &p); + if (!default_ei) { + /* Nothing to be done here. */ + if (IS_DEBUG_OSPF_DEFAULT_INFO) + zlog_debug("Default originate info not present"); + return; + } + + /* For all the ei apply route-map */ + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { + ext_list = ospf->external[type]; + if (!ext_list || type == ZEBRA_ROUTE_OSPF) + continue; + + ret = ospf_external_default_routemap_apply_walk(ospf, ext_list, + default_ei); + if (ret) + break; + } + + /* Get the default LSA. */ + lsa = ospf_external_info_find_lsa(ospf, &p); + + /* If permit then originate default. */ + if (ret && !lsa) + ospf_external_lsa_originate(ospf, default_ei); + else if (ret && lsa && IS_LSA_MAXAGE(lsa)) + ospf_external_lsa_refresh(ospf, lsa, default_ei, true, false); + else if (!ret && lsa) + ospf_external_lsa_flush(ospf, DEFAULT_ROUTE, &default_ei->p, 0); +} + + +void ospf_external_del(struct ospf *ospf, uint8_t type, unsigned short instance) +{ + struct ospf_external *ext; + + ext = ospf_external_lookup(ospf, type, instance); + + if (ext) { + if (EXTERNAL_INFO(ext)) + route_table_finish(EXTERNAL_INFO(ext)); + + listnode_delete(ospf->external[type], ext); + + if (!ospf->external[type]->count) + list_delete(&ospf->external[type]); + + XFREE(MTYPE_OSPF_EXTERNAL, ext); + } + + /* + * Check if default needs to be flushed too. + */ + event_add_event(master, ospf_external_lsa_default_routemap_timer, ospf, + 0, &ospf->t_default_routemap_timer); +} + +/* Update NHLFE for Prefix SID */ +void ospf_zebra_update_prefix_sid(const struct sr_prefix *srp) +{ + struct zapi_labels zl; + struct zapi_nexthop *znh; + struct zapi_nexthop *znh_backup; + struct listnode *node; + struct ospf_path *path; + + /* Prepare message. */ + memset(&zl, 0, sizeof(zl)); + zl.type = ZEBRA_LSP_OSPF_SR; + zl.local_label = srp->label_in; + + switch (srp->type) { + case LOCAL_SID: + /* Set Label for local Prefix */ + znh = &zl.nexthops[zl.nexthop_num++]; + znh->type = NEXTHOP_TYPE_IFINDEX; + znh->ifindex = srp->nhlfe.ifindex; + znh->label_num = 1; + znh->labels[0] = srp->nhlfe.label_out; + + osr_debug("SR (%s): Configure Prefix %pFX with labels %u/%u", + __func__, (struct prefix *)&srp->prefv4, + srp->label_in, srp->nhlfe.label_out); + + break; + + case PREF_SID: + /* Update route in the RIB too. */ + SET_FLAG(zl.message, ZAPI_LABELS_FTN); + zl.route.prefix.u.prefix4 = srp->prefv4.prefix; + zl.route.prefix.prefixlen = srp->prefv4.prefixlen; + zl.route.prefix.family = srp->prefv4.family; + zl.route.type = ZEBRA_ROUTE_OSPF; + zl.route.instance = 0; + + /* Check that SRP contains at least one valid path */ + if (srp->route == NULL) { + return; + } + + osr_debug("SR (%s): Configure Prefix %pFX with", + __func__, (struct prefix *)&srp->prefv4); + + for (ALL_LIST_ELEMENTS_RO(srp->route->paths, node, path)) { + if (path->srni.label_out == MPLS_INVALID_LABEL) + continue; + + if (zl.nexthop_num >= MULTIPATH_NUM) + break; + + /* + * TI-LFA backup path label stack comes first, if + * present. + */ + if (path->srni.backup_label_stack) { + znh_backup = &zl.backup_nexthops + [zl.backup_nexthop_num++]; + znh_backup->type = NEXTHOP_TYPE_IPV4; + znh_backup->gate.ipv4 = + path->srni.backup_nexthop; + + memcpy(znh_backup->labels, + path->srni.backup_label_stack->label, + sizeof(mpls_label_t) + * path->srni.backup_label_stack + ->num_labels); + + znh_backup->label_num = + path->srni.backup_label_stack + ->num_labels; + if (path->srni.label_out + != MPLS_LABEL_IPV4_EXPLICIT_NULL + && path->srni.label_out + != MPLS_LABEL_IMPLICIT_NULL) + znh_backup->labels + [znh_backup->label_num++] = + path->srni.label_out; + } + + znh = &zl.nexthops[zl.nexthop_num++]; + znh->type = NEXTHOP_TYPE_IPV4_IFINDEX; + znh->gate.ipv4 = path->nexthop; + znh->ifindex = path->ifindex; + znh->label_num = 1; + znh->labels[0] = path->srni.label_out; + + osr_debug(" |- labels %u/%u", srp->label_in, + path->srni.label_out); + + /* Set TI-LFA backup nexthop info if present */ + if (path->srni.backup_label_stack) { + SET_FLAG(zl.message, ZAPI_LABELS_HAS_BACKUPS); + SET_FLAG(znh->flags, + ZAPI_NEXTHOP_FLAG_HAS_BACKUP); + + /* Just care about a single TI-LFA backup path + * for now */ + znh->backup_num = 1; + znh->backup_idx[0] = zl.backup_nexthop_num - 1; + } + } + break; + case ADJ_SID: + case LAN_ADJ_SID: + return; + } + + /* Finally, send message to zebra. */ + (void)zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_REPLACE, &zl); +} + +/* Remove NHLFE for Prefix-SID */ +void ospf_zebra_delete_prefix_sid(const struct sr_prefix *srp) +{ + struct zapi_labels zl; + + osr_debug("SR (%s): Delete Labels %u for Prefix %pFX", __func__, + srp->label_in, (struct prefix *)&srp->prefv4); + + /* Prepare message. */ + memset(&zl, 0, sizeof(zl)); + zl.type = ZEBRA_LSP_OSPF_SR; + zl.local_label = srp->label_in; + + if (srp->type == PREF_SID) { + /* Update route in the RIB too */ + SET_FLAG(zl.message, ZAPI_LABELS_FTN); + zl.route.prefix.u.prefix4 = srp->prefv4.prefix; + zl.route.prefix.prefixlen = srp->prefv4.prefixlen; + zl.route.prefix.family = srp->prefv4.family; + zl.route.type = ZEBRA_ROUTE_OSPF; + zl.route.instance = 0; + } + + /* Send message to zebra. */ + (void)zebra_send_mpls_labels(zclient, ZEBRA_MPLS_LABELS_DELETE, &zl); +} + +/* Send MPLS Label entry to Zebra for installation or deletion */ +void ospf_zebra_send_adjacency_sid(int cmd, struct sr_nhlfe nhlfe) +{ + struct zapi_labels zl; + struct zapi_nexthop *znh; + + osr_debug("SR (%s): %s Labels %u/%u for Adjacency via %u", __func__, + cmd == ZEBRA_MPLS_LABELS_ADD ? "Add" : "Delete", + nhlfe.label_in, nhlfe.label_out, nhlfe.ifindex); + + memset(&zl, 0, sizeof(zl)); + zl.type = ZEBRA_LSP_OSPF_SR; + zl.local_label = nhlfe.label_in; + zl.nexthop_num = 1; + znh = &zl.nexthops[0]; + znh->type = NEXTHOP_TYPE_IPV4_IFINDEX; + znh->gate.ipv4 = nhlfe.nexthop; + znh->ifindex = nhlfe.ifindex; + znh->label_num = 1; + znh->labels[0] = nhlfe.label_out; + + (void)zebra_send_mpls_labels(zclient, cmd, &zl); +} + +struct ospf_redist *ospf_redist_lookup(struct ospf *ospf, uint8_t type, + unsigned short instance) +{ + struct list *red_list; + struct listnode *node; + struct ospf_redist *red; + + red_list = ospf->redist[type]; + if (!red_list) + return (NULL); + + for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) + if (red->instance == instance) + return red; + + return NULL; +} + +struct ospf_redist *ospf_redist_add(struct ospf *ospf, uint8_t type, + unsigned short instance) +{ + struct list *red_list; + struct ospf_redist *red; + + red = ospf_redist_lookup(ospf, type, instance); + if (red) + return red; + + if (!ospf->redist[type]) + ospf->redist[type] = list_new(); + + red_list = ospf->redist[type]; + red = XCALLOC(MTYPE_OSPF_REDISTRIBUTE, sizeof(struct ospf_redist)); + red->instance = instance; + red->dmetric.type = -1; + red->dmetric.value = -1; + ROUTEMAP_NAME(red) = NULL; + ROUTEMAP(red) = NULL; + + listnode_add(red_list, red); + + return red; +} + +void ospf_redist_del(struct ospf *ospf, uint8_t type, unsigned short instance) +{ + struct ospf_redist *red; + + red = ospf_redist_lookup(ospf, type, instance); + + if (red) { + listnode_delete(ospf->redist[type], red); + if (!ospf->redist[type]->count) { + list_delete(&ospf->redist[type]); + } + ospf_routemap_unset(red); + XFREE(MTYPE_OSPF_REDISTRIBUTE, red); + } +} + + +int ospf_is_type_redistributed(struct ospf *ospf, int type, + unsigned short instance) +{ + return (DEFAULT_ROUTE_TYPE(type) + ? vrf_bitmap_check( + &zclient->default_information[AFI_IP], + ospf->vrf_id) + : ((instance && + redist_check_instance( + &zclient->mi_redist[AFI_IP][type], + instance)) || + (!instance && + vrf_bitmap_check(&zclient->redist[AFI_IP][type], + ospf->vrf_id)))); +} + +int ospf_redistribute_update(struct ospf *ospf, struct ospf_redist *red, + int type, unsigned short instance, int mtype, + int mvalue) +{ + int force = 0; + + if (mtype != red->dmetric.type) { + red->dmetric.type = mtype; + force = LSA_REFRESH_FORCE; + } + if (mvalue != red->dmetric.value) { + red->dmetric.value = mvalue; + force = LSA_REFRESH_FORCE; + } + + ospf_external_lsa_refresh_type(ospf, type, instance, force); + + if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug( + "Redistribute[%s][%d]: Refresh Type[%d], Metric[%d]", + ospf_redist_string(type), instance, + metric_type(ospf, type, instance), + metric_value(ospf, type, instance)); + + return CMD_SUCCESS; +} + +int ospf_redistribute_set(struct ospf *ospf, struct ospf_redist *red, int type, + unsigned short instance, int mtype, int mvalue) +{ + red->dmetric.type = mtype; + red->dmetric.value = mvalue; + + ospf_external_add(ospf, type, instance); + + zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, type, + instance, ospf->vrf_id); + + if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug( + "Redistribute[%s][%d] vrf id %u: Start Type[%d], Metric[%d]", + ospf_redist_string(type), instance, ospf->vrf_id, + metric_type(ospf, type, instance), + metric_value(ospf, type, instance)); + + ospf_asbr_status_update(ospf, ++ospf->redistribute); + + return CMD_SUCCESS; +} + +int ospf_redistribute_unset(struct ospf *ospf, int type, + unsigned short instance) +{ + if (type == zclient->redist_default && instance == zclient->instance) + return CMD_SUCCESS; + + zclient_redistribute(ZEBRA_REDISTRIBUTE_DELETE, zclient, AFI_IP, type, + instance, ospf->vrf_id); + + if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug("Redistribute[%s][%d] vrf id %u: Stop", + ospf_redist_string(type), instance, ospf->vrf_id); + + /* Remove the routes from OSPF table. */ + ospf_redistribute_withdraw(ospf, type, instance); + + ospf_external_del(ospf, type, instance); + + ospf_asbr_status_update(ospf, --ospf->redistribute); + + return CMD_SUCCESS; +} + +int ospf_redistribute_default_set(struct ospf *ospf, int originate, int mtype, + int mvalue) +{ + struct prefix_ipv4 p; + struct in_addr nexthop; + int cur_originate = ospf->default_originate; + const char *type_str = NULL; + + nexthop.s_addr = INADDR_ANY; + p.family = AF_INET; + p.prefix.s_addr = INADDR_ANY; + p.prefixlen = 0; + + ospf->default_originate = originate; + + if (cur_originate == originate) { + /* Refresh the lsa since metric might different */ + if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug( + "Redistribute[%s]: Refresh Type[%d], Metric[%d]", + ospf_redist_string(DEFAULT_ROUTE), + metric_type(ospf, DEFAULT_ROUTE, 0), + metric_value(ospf, DEFAULT_ROUTE, 0)); + + ospf_external_lsa_refresh_default(ospf); + return CMD_SUCCESS; + } + + switch (cur_originate) { + case DEFAULT_ORIGINATE_NONE: + break; + case DEFAULT_ORIGINATE_ZEBRA: + zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, + zclient, AFI_IP, ospf->vrf_id); + ospf->redistribute--; + break; + case DEFAULT_ORIGINATE_ALWAYS: + ospf_external_info_delete(ospf, DEFAULT_ROUTE, 0, p); + ospf_external_del(ospf, DEFAULT_ROUTE, 0); + ospf->redistribute--; + break; + } + + switch (originate) { + case DEFAULT_ORIGINATE_NONE: + type_str = "none"; + break; + case DEFAULT_ORIGINATE_ZEBRA: + type_str = "normal"; + ospf->redistribute++; + zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_ADD, + zclient, AFI_IP, ospf->vrf_id); + break; + case DEFAULT_ORIGINATE_ALWAYS: + type_str = "always"; + ospf->redistribute++; + ospf_external_add(ospf, DEFAULT_ROUTE, 0); + ospf_external_info_add(ospf, DEFAULT_ROUTE, 0, p, 0, nexthop, 0, + DEFAULT_DEFAULT_METRIC); + break; + } + + if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug("Redistribute[DEFAULT]: %s Type[%d], Metric[%d]", + type_str, + metric_type(ospf, DEFAULT_ROUTE, 0), + metric_value(ospf, DEFAULT_ROUTE, 0)); + + ospf_external_lsa_refresh_default(ospf); + ospf_asbr_status_update(ospf, ospf->redistribute); + return CMD_SUCCESS; +} + +static int ospf_external_lsa_originate_check(struct ospf *ospf, + struct external_info *ei) +{ + /* If prefix is multicast, then do not originate LSA. */ + if (IN_MULTICAST(htonl(ei->p.prefix.s_addr))) { + zlog_info( + "LSA[Type5:%pI4]: Not originate AS-external-LSA, Prefix belongs multicast", + &ei->p.prefix); + return 0; + } + + /* Take care of default-originate. */ + if (is_default_prefix4(&ei->p)) + if (ospf->default_originate == DEFAULT_ORIGINATE_NONE) { + zlog_info( + "LSA[Type5:0.0.0.0]: Not originate AS-external-LSA for default"); + return 0; + } + + return 1; +} + +/* If connected prefix is OSPF enable interface, then do not announce. */ +int ospf_distribute_check_connected(struct ospf *ospf, struct external_info *ei) +{ + struct listnode *node; + struct ospf_interface *oi; + + + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) + if (prefix_match(oi->address, (struct prefix *)&ei->p)) + return 0; + return 1; +} + + +/* Apply default route-map on ei received. */ +int ospf_external_info_apply_default_routemap(struct ospf *ospf, + struct external_info *ei, + struct external_info *default_ei) +{ + struct ospf_redist *red; + int type = default_ei->type; + struct prefix_ipv4 *p = &ei->p; + struct route_map_set_values save_values; + + + if (!ospf_external_lsa_originate_check(ospf, default_ei)) + return 0; + + save_values = default_ei->route_map_set; + ospf_reset_route_map_set_values(&default_ei->route_map_set); + + /* apply route-map if needed */ + red = ospf_redist_lookup(ospf, type, ospf->instance); + if (red && ROUTEMAP_NAME(red)) { + route_map_result_t ret; + + ret = route_map_apply(ROUTEMAP(red), (struct prefix *)p, ei); + + if (ret == RMAP_DENYMATCH) { + ei->route_map_set = save_values; + return 0; + } + } + + return 1; +} + + +/* + * Default originated is based on route-map condition then + * apply route-map on received external info. Originate or + * flush based on route-map condition. + */ +static bool ospf_external_lsa_default_routemap_apply(struct ospf *ospf, + struct external_info *ei, + int cmd) +{ + struct external_info *default_ei; + struct prefix_ipv4 p; + struct ospf_lsa *lsa; + int ret; + + p.family = AF_INET; + p.prefixlen = 0; + p.prefix.s_addr = INADDR_ANY; + + + /* Get the default extenal info. */ + default_ei = ospf_external_info_lookup(ospf, DEFAULT_ROUTE, + ospf->instance, &p); + if (!default_ei) { + /* Nothing to be done here. */ + return false; + } + + if (IS_DEBUG_OSPF_DEFAULT_INFO) + zlog_debug("Apply default originate routemap on ei: %pI4 cmd: %d", + &ei->p.prefix, cmd); + + ret = ospf_external_info_apply_default_routemap(ospf, ei, default_ei); + + /* If deny then nothing to be done both in add and del case. */ + if (!ret) { + if (IS_DEBUG_OSPF_DEFAULT_INFO) + zlog_debug("Default originte routemap deny for ei: %pI4", + &ei->p.prefix); + return false; + } + + /* Get the default LSA. */ + lsa = ospf_external_info_find_lsa(ospf, &p); + + /* If this is add route and permit then ooriginate default. */ + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { + /* If permit and default already advertise then return. */ + if (lsa && !IS_LSA_MAXAGE(lsa)) { + if (IS_DEBUG_OSPF_DEFAULT_INFO) + zlog_debug("Default lsa already originated"); + return true; + } + + if (IS_DEBUG_OSPF_DEFAULT_INFO) + zlog_debug("Originating/Refreshing default lsa"); + + if (lsa && IS_LSA_MAXAGE(lsa)) + /* Refresh lsa.*/ + ospf_external_lsa_refresh(ospf, lsa, default_ei, true, + false); + else + /* If permit and default not advertised then advertise. + */ + ospf_external_lsa_originate(ospf, default_ei); + + } else if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) { + /* If deny and lsa is not originated then nothing to be done.*/ + if (!lsa) { + if (IS_DEBUG_OSPF_DEFAULT_INFO) + zlog_debug( + "Default lsa not originated, not flushing"); + return true; + } + + if (IS_DEBUG_OSPF_DEFAULT_INFO) + zlog_debug( + "Running default route-map again as ei: %pI4 deleted", + &ei->p.prefix); + /* + * if this route delete was permitted then we need to check + * there are any other external info which can still trigger + * default route origination else flush it. + */ + event_add_event(master, + ospf_external_lsa_default_routemap_timer, ospf, + 0, &ospf->t_default_routemap_timer); + } + + return true; +} + +/* return 1 if external LSA must be originated, 0 otherwise */ +int ospf_redistribute_check(struct ospf *ospf, struct external_info *ei, + int *changed) +{ + struct route_map_set_values save_values; + struct prefix_ipv4 *p = &ei->p; + struct ospf_redist *red; + uint8_t type = is_default_prefix4(&ei->p) ? DEFAULT_ROUTE : ei->type; + unsigned short instance = is_default_prefix4(&ei->p) ? 0 : ei->instance; + route_tag_t saved_tag = 0; + + /* Default is handled differently. */ + if (type == DEFAULT_ROUTE) + return 1; + + if (changed) + *changed = 0; + + if (!ospf_external_lsa_originate_check(ospf, ei)) + return 0; + + /* Take care connected route. */ + if (type == ZEBRA_ROUTE_CONNECT + && !ospf_distribute_check_connected(ospf, ei)) + return 0; + + if (!DEFAULT_ROUTE_TYPE(type) && DISTRIBUTE_NAME(ospf, type)) + /* distirbute-list exists, but access-list may not? */ + if (DISTRIBUTE_LIST(ospf, type)) + if (access_list_apply(DISTRIBUTE_LIST(ospf, type), p) + == FILTER_DENY) { + if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug( + "Redistribute[%s]: %pFX filtered by distribute-list.", + ospf_redist_string(type), p); + return 0; + } + + save_values = ei->route_map_set; + ospf_reset_route_map_set_values(&ei->route_map_set); + + saved_tag = ei->tag; + /* Resetting with original route tag */ + ei->tag = ei->orig_tag; + + /* apply route-map if needed */ + red = ospf_redist_lookup(ospf, type, instance); + if (red && ROUTEMAP_NAME(red)) { + route_map_result_t ret; + + ret = route_map_apply(ROUTEMAP(red), (struct prefix *)p, ei); + + if (ret == RMAP_DENYMATCH) { + ei->route_map_set = save_values; + if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug( + "Redistribute[%s]: %pFX filtered by route-map.", + ospf_redist_string(type), p); + return 0; + } + + /* check if 'route-map set' changed something */ + if (changed) { + *changed = !ospf_route_map_set_compare( + &ei->route_map_set, &save_values); + + /* check if tag is modified */ + *changed |= (saved_tag != ei->tag); + } + } + + return 1; +} + +/* OSPF route-map set for redistribution */ +void ospf_routemap_set(struct ospf_redist *red, const char *name) +{ + if (ROUTEMAP_NAME(red)) { + route_map_counter_decrement(ROUTEMAP(red)); + free(ROUTEMAP_NAME(red)); + } + + ROUTEMAP_NAME(red) = strdup(name); + ROUTEMAP(red) = route_map_lookup_by_name(name); + route_map_counter_increment(ROUTEMAP(red)); +} + +void ospf_routemap_unset(struct ospf_redist *red) +{ + if (ROUTEMAP_NAME(red)) { + route_map_counter_decrement(ROUTEMAP(red)); + free(ROUTEMAP_NAME(red)); + } + + ROUTEMAP_NAME(red) = NULL; + ROUTEMAP(red) = NULL; +} + +static int ospf_zebra_gr_update(struct ospf *ospf, int command, + uint32_t stale_time) +{ + struct zapi_cap api; + + if (!zclient || zclient->sock < 0 || !ospf) + return 1; + + memset(&api, 0, sizeof(api)); + api.cap = command; + api.stale_removal_time = stale_time; + api.vrf_id = ospf->vrf_id; + + (void)zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient, + &api); + + return 0; +} + +int ospf_zebra_gr_enable(struct ospf *ospf, uint32_t stale_time) +{ + if (IS_DEBUG_OSPF_GR) + zlog_debug("Zebra enable GR [stale time %u]", stale_time); + + return ospf_zebra_gr_update(ospf, ZEBRA_CLIENT_GR_CAPABILITIES, + stale_time); +} + +int ospf_zebra_gr_disable(struct ospf *ospf) +{ + if (IS_DEBUG_OSPF_GR) + zlog_debug("Zebra disable GR"); + + return ospf_zebra_gr_update(ospf, ZEBRA_CLIENT_GR_DISABLE, 0); +} + +/* Zebra route add and delete treatment. */ +static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS) +{ + struct zapi_route api; + struct prefix_ipv4 p; + struct prefix pgen; + unsigned long ifindex; + struct in_addr nexthop; + struct external_info *ei; + struct ospf *ospf; + int i; + uint8_t rt_type; + + ospf = ospf_lookup_by_vrf_id(vrf_id); + if (ospf == NULL) + return 0; + + if (zapi_route_decode(zclient->ibuf, &api) < 0) + return -1; + + ifindex = api.nexthops[0].ifindex; + nexthop = api.nexthops[0].gate.ipv4; + rt_type = api.type; + + memcpy(&p, &api.prefix, sizeof(p)); + if (IPV4_NET127(ntohl(p.prefix.s_addr))) + return 0; + + pgen.family = p.family; + pgen.prefixlen = p.prefixlen; + pgen.u.prefix4 = p.prefix; + + /* Re-destributed route is default route. + * Here, route type is used as 'ZEBRA_ROUTE_KERNEL' for + * updating ex-info. But in resetting (no default-info + * originate)ZEBRA_ROUTE_MAX is used to delete the ex-info. + * Resolved this inconsistency by maintaining same route type. + */ + if ((is_default_prefix(&pgen)) && (api.type != ZEBRA_ROUTE_OSPF)) + rt_type = DEFAULT_ROUTE; + + if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) + zlog_debug( + "%s: cmd %s from client %s: vrf_id %d, p %pFX, metric %d", + __func__, zserv_command_string(cmd), + zebra_route_string(api.type), vrf_id, &api.prefix, + api.metric); + + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { + /* XXX|HACK|TODO|FIXME: + * Maybe we should ignore reject/blackhole routes? Testing + * shows that there is no problems though and this is only way + * to "summarize" routes in ASBR at the moment. Maybe we need + * just a better generalised solution for these types? + */ + + /* Protocol tag overwrites all other tag value sent by zebra */ + if (ospf->dtag[rt_type] > 0) + api.tag = ospf->dtag[rt_type]; + + /* + * Given zebra sends update for a prefix via ADD message, it + * should + * be considered as an implicit DEL for that prefix with other + * source + * types. + */ + for (i = 0; i <= ZEBRA_ROUTE_MAX; i++) + if (i != rt_type) + ospf_external_info_delete(ospf, i, api.instance, + p); + + ei = ospf_external_info_add(ospf, rt_type, api.instance, p, + ifindex, nexthop, api.tag, + api.metric); + if (ei == NULL) { + /* Nothing has changed, so nothing to do; return */ + return 0; + } + if (ospf->router_id.s_addr != INADDR_ANY) { + if (is_default_prefix4(&p)) + ospf_external_lsa_refresh_default(ospf); + else { + struct ospf_external_aggr_rt *aggr; + struct as_external_lsa *al; + struct ospf_lsa *lsa = NULL; + struct in_addr mask; + + aggr = ospf_external_aggr_match(ospf, &ei->p); + + if (aggr) { + /* Check the AS-external-LSA + * should be originated. + */ + if (!ospf_redistribute_check(ospf, ei, + NULL)) + return 0; + + if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) + zlog_debug( + "%s: Send Aggreate LSA (%pI4/%d)", + __func__, + &aggr->p.prefix, + aggr->p.prefixlen); + + ospf_originate_summary_lsa(ospf, aggr, + ei); + + /* Handling the case where the + * external route prefix + * and aggegate prefix is same + * If same don't flush the + * originated + * external LSA. + */ + if (prefix_same( + (struct prefix *)&aggr->p, + (struct prefix *)&ei->p)) + return 0; + + lsa = ospf_external_info_find_lsa( + ospf, &ei->p); + + if (lsa) { + al = (struct as_external_lsa *) + lsa->data; + masklen2ip(ei->p.prefixlen, + &mask); + + if (mask.s_addr + != al->mask.s_addr) + return 0; + + ospf_external_lsa_flush( + ospf, ei->type, &ei->p, + 0); + } + } else { + struct ospf_lsa *current; + + current = ospf_external_info_find_lsa( + ospf, &ei->p); + if (!current) { + /* Check the + * AS-external-LSA + * should be + * originated. + */ + if (!ospf_redistribute_check( + ospf, ei, NULL)) + return 0; + + ospf_external_lsa_originate( + ospf, ei); + } else { + if (IS_DEBUG_OSPF( + zebra, + ZEBRA_REDISTRIBUTE)) + zlog_debug( + "%s: %pI4 refreshing LSA", + __func__, + &p.prefix); + ospf_external_lsa_refresh( + ospf, current, ei, + LSA_REFRESH_FORCE, + false); + } + } + } + } + + /* + * Check if default-information originate is + * with some routemap prefix/access list match. + */ + ospf_external_lsa_default_routemap_apply(ospf, ei, cmd); + + } else { /* if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */ + struct ospf_external_aggr_rt *aggr; + + ei = ospf_external_info_lookup(ospf, rt_type, api.instance, &p); + if (ei == NULL) + return 0; + + /* + * Check if default-information originate i + * with some routemap prefix/access list match. + * Apply before ei is deleted. + */ + ospf_external_lsa_default_routemap_apply(ospf, ei, cmd); + + aggr = ospf_external_aggr_match(ospf, &ei->p); + + if (aggr && (ei->aggr_route == aggr)) { + ospf_unlink_ei_from_aggr(ospf, aggr, ei); + + ospf_external_info_delete(ospf, rt_type, api.instance, + p); + } else { + ospf_external_info_delete(ospf, rt_type, api.instance, + p); + + if (is_default_prefix4(&p)) + ospf_external_lsa_refresh_default(ospf); + else + ospf_external_lsa_flush(ospf, rt_type, &p, + ifindex /*, nexthop */); + } + } + + return 0; +} + +void ospf_zebra_import_default_route(struct ospf *ospf, bool unreg) +{ + struct prefix prefix = {}; + int command; + + if (zclient->sock < 0) { + if (IS_DEBUG_OSPF(zebra, ZEBRA)) + zlog_debug(" Not connected to Zebra"); + return; + } + + prefix.family = AF_INET; + prefix.prefixlen = 0; + + if (unreg) + command = ZEBRA_NEXTHOP_UNREGISTER; + else + command = ZEBRA_NEXTHOP_REGISTER; + + if (IS_DEBUG_OSPF(zebra, ZEBRA)) + zlog_debug("%s: sending cmd %s for %pFX (vrf %u)", __func__, + zserv_command_string(command), &prefix, + ospf->vrf_id); + + if (zclient_send_rnh(zclient, command, &prefix, SAFI_UNICAST, false, + true, ospf->vrf_id) == ZCLIENT_SEND_FAILURE) + flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient_send_rnh() failed", + __func__); +} + +static int ospf_zebra_import_check_update(ZAPI_CALLBACK_ARGS) +{ + struct ospf *ospf; + struct zapi_route nhr; + struct prefix matched; + + ospf = ospf_lookup_by_vrf_id(vrf_id); + if (ospf == NULL || !IS_OSPF_ASBR(ospf)) + return 0; + + if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) { + zlog_err("%s[%u]: Failure to decode route", __func__, + ospf->vrf_id); + return -1; + } + + if (matched.family != AF_INET || matched.prefixlen != 0 || + nhr.type == ZEBRA_ROUTE_OSPF) + return 0; + + ospf->nssa_default_import_check.status = !!nhr.nexthop_num; + ospf_abr_nssa_type7_defaults(ospf); + + return 0; +} + +int ospf_distribute_list_out_set(struct ospf *ospf, int type, const char *name) +{ + /* Lookup access-list for distribute-list. */ + DISTRIBUTE_LIST(ospf, type) = access_list_lookup(AFI_IP, name); + + /* Clear previous distribute-name. */ + if (DISTRIBUTE_NAME(ospf, type)) + free(DISTRIBUTE_NAME(ospf, type)); + + /* Set distribute-name. */ + DISTRIBUTE_NAME(ospf, type) = strdup(name); + + /* If access-list have been set, schedule update timer. */ + if (DISTRIBUTE_LIST(ospf, type)) + ospf_distribute_list_update(ospf, type, 0); + + return CMD_SUCCESS; +} + +int ospf_distribute_list_out_unset(struct ospf *ospf, int type, + const char *name) +{ + /* Schedule update timer. */ + if (DISTRIBUTE_LIST(ospf, type)) + ospf_distribute_list_update(ospf, type, 0); + + /* Unset distribute-list. */ + DISTRIBUTE_LIST(ospf, type) = NULL; + + /* Clear distribute-name. */ + if (DISTRIBUTE_NAME(ospf, type)) + free(DISTRIBUTE_NAME(ospf, type)); + + DISTRIBUTE_NAME(ospf, type) = NULL; + + return CMD_SUCCESS; +} + +/* distribute-list update timer. */ +static void ospf_distribute_list_update_timer(struct event *thread) +{ + struct route_node *rn; + struct external_info *ei; + struct route_table *rt; + struct ospf_lsa *lsa; + int type, default_refresh = 0; + struct ospf *ospf = EVENT_ARG(thread); + + if (ospf == NULL) + return; + + ospf->t_distribute_update = NULL; + + zlog_info("Zebra[Redistribute]: distribute-list update timer fired!"); + + if (IS_DEBUG_OSPF_EVENT) { + zlog_debug("%s: ospf distribute-list update vrf %s id %d", + __func__, ospf_vrf_id_to_name(ospf->vrf_id), + ospf->vrf_id); + } + + /* foreach all external info. */ + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { + struct list *ext_list; + struct listnode *node; + struct ospf_external *ext; + + ext_list = ospf->external[type]; + if (!ext_list) + continue; + + for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) { + rt = ext->external_info; + if (!rt) + continue; + for (rn = route_top(rt); rn; rn = route_next(rn)) { + ei = rn->info; + if (!ei) + continue; + + if (is_default_prefix4(&ei->p)) + default_refresh = 1; + else { + struct ospf_external_aggr_rt *aggr; + + aggr = ospf_external_aggr_match(ospf, + &ei->p); + if (aggr) { + /* Check the + * AS-external-LSA + * should be originated. + */ + if (!ospf_redistribute_check( + ospf, ei, NULL)) { + + ospf_unlink_ei_from_aggr( + ospf, aggr, ei); + continue; + } + + if (IS_DEBUG_OSPF( + lsa, + EXTNL_LSA_AGGR)) + zlog_debug( + "%s: Send Aggregate LSA (%pI4/%d)", + __func__, + &aggr->p.prefix, + aggr->p.prefixlen); + + /* Originate Aggregate + * LSA + */ + ospf_originate_summary_lsa( + ospf, aggr, ei); + } else if ( + (lsa = ospf_external_info_find_lsa( + ospf, &ei->p))) { + int force = + LSA_REFRESH_IF_CHANGED; + /* If this is a MaxAge + * LSA, we need to + * force refresh it + * because distribute + * settings might have + * changed and now, + * this LSA needs to be + * originated, not be + * removed. + * If we don't force + * refresh it, it will + * remain a MaxAge LSA + * because it will look + * like it hasn't + * changed. Neighbors + * will not receive + * updates for this LSA. + */ + if (IS_LSA_MAXAGE(lsa)) + force = LSA_REFRESH_FORCE; + + ospf_external_lsa_refresh( + ospf, lsa, ei, force, + false); + } else { + if (!ospf_redistribute_check( + ospf, ei, NULL)) + continue; + ospf_external_lsa_originate( + ospf, ei); + } + } + } + } + } + if (default_refresh) + ospf_external_lsa_refresh_default(ospf); +} + +/* Update distribute-list and set timer to apply access-list. */ +void ospf_distribute_list_update(struct ospf *ospf, int type, + unsigned short instance) +{ + struct ospf_external *ext; + + /* External info does not exist. */ + ext = ospf_external_lookup(ospf, type, instance); + if (!ext || !EXTERNAL_INFO(ext)) + return; + + /* Set timer. If timer is already started, this call does nothing. */ + event_add_timer_msec(master, ospf_distribute_list_update_timer, ospf, + ospf->min_ls_interval, &ospf->t_distribute_update); +} + +/* If access-list is updated, apply some check. */ +static void ospf_filter_update(struct access_list *access) +{ + struct ospf *ospf; + int type; + int abr_inv = 0; + struct ospf_area *area; + struct listnode *node, *n1; + + /* If OSPF instance does not exist, return right now. */ + if (listcount(om->ospf) == 0) + return; + + /* Iterate all ospf [VRF] instances */ + for (ALL_LIST_ELEMENTS_RO(om->ospf, n1, ospf)) { + /* Update distribute-list, and apply filter. */ + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { + struct list *red_list; + struct ospf_redist *red; + + red_list = ospf->redist[type]; + if (red_list) + for (ALL_LIST_ELEMENTS_RO(red_list, node, + red)) { + if (ROUTEMAP(red)) { + /* if route-map is not NULL it + * may be + * using this access list */ + ospf_distribute_list_update( + ospf, type, + red->instance); + } + } + + /* There is place for route-map for default-information + * (ZEBRA_ROUTE_MAX), + * but no distribute list. */ + if (type == ZEBRA_ROUTE_MAX) + break; + + if (DISTRIBUTE_NAME(ospf, type)) { + /* Keep old access-list for distribute-list. */ + struct access_list *old = + DISTRIBUTE_LIST(ospf, type); + + /* Update access-list for distribute-list. */ + DISTRIBUTE_LIST(ospf, type) = + access_list_lookup( + AFI_IP, + DISTRIBUTE_NAME(ospf, type)); + + /* No update for this distribute type. */ + if (old == NULL + && DISTRIBUTE_LIST(ospf, type) == NULL) + continue; + + /* Schedule distribute-list update timer. */ + if (DISTRIBUTE_LIST(ospf, type) == NULL + || strcmp(DISTRIBUTE_NAME(ospf, type), + access->name) + == 0) + ospf_distribute_list_update(ospf, type, + 0); + } + } + + /* Update Area access-list. */ + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + if (EXPORT_NAME(area)) { + EXPORT_LIST(area) = NULL; + abr_inv++; + } + + if (IMPORT_NAME(area)) { + IMPORT_LIST(area) = NULL; + abr_inv++; + } + } + + /* Schedule ABR tasks -- this will be changed -- takada. */ + if (IS_OSPF_ABR(ospf) && abr_inv) + ospf_schedule_abr_task(ospf); + } +} + +/* If prefix-list is updated, do some updates. */ +static void ospf_prefix_list_update(struct prefix_list *plist) +{ + struct ospf *ospf = NULL; + int type; + int abr_inv = 0; + struct ospf_area *area; + struct listnode *node, *n1; + + /* If OSPF instatnce does not exist, return right now. */ + if (listcount(om->ospf) == 0) + return; + + /* Iterate all ospf [VRF] instances */ + for (ALL_LIST_ELEMENTS_RO(om->ospf, n1, ospf)) { + + /* Update all route-maps which are used + * as redistribution filters. + * They might use prefix-list. + */ + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { + struct list *red_list; + struct ospf_redist *red; + + red_list = ospf->redist[type]; + if (!red_list) + continue; + + for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) { + if (ROUTEMAP(red)) { + /* if route-map is not NULL + * it may be using + * this prefix list */ + ospf_distribute_list_update( + ospf, type, red->instance); + } + } + } + + /* Update area filter-lists. */ + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + /* Update filter-list in. */ + if (PREFIX_NAME_IN(area) + && strcmp(PREFIX_NAME_IN(area), + prefix_list_name(plist)) + == 0) { + PREFIX_LIST_IN(area) = prefix_list_lookup( + AFI_IP, PREFIX_NAME_IN(area)); + abr_inv++; + } + + /* Update filter-list out. */ + if (PREFIX_NAME_OUT(area) + && strcmp(PREFIX_NAME_OUT(area), + prefix_list_name(plist)) + == 0) { + PREFIX_LIST_OUT(area) = prefix_list_lookup( + AFI_IP, PREFIX_NAME_OUT(area)); + abr_inv++; + } + } + + /* Schedule ABR task. */ + if (IS_OSPF_ABR(ospf) && abr_inv) + ospf_schedule_abr_task(ospf); + } +} + +static struct ospf_distance *ospf_distance_new(void) +{ + return XCALLOC(MTYPE_OSPF_DISTANCE, sizeof(struct ospf_distance)); +} + +static void ospf_distance_free(struct ospf_distance *odistance) +{ + XFREE(MTYPE_OSPF_DISTANCE, odistance); +} + +int ospf_distance_set(struct vty *vty, struct ospf *ospf, + const char *distance_str, const char *ip_str, + const char *access_list_str) +{ + int ret; + struct prefix_ipv4 p; + uint8_t distance; + struct route_node *rn; + struct ospf_distance *odistance; + + ret = str2prefix_ipv4(ip_str, &p); + if (ret == 0) { + vty_out(vty, "Malformed prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + distance = atoi(distance_str); + + /* Get OSPF distance node. */ + rn = route_node_get(ospf->distance_table, (struct prefix *)&p); + if (rn->info) { + odistance = rn->info; + route_unlock_node(rn); + } else { + odistance = ospf_distance_new(); + rn->info = odistance; + } + + /* Set distance value. */ + odistance->distance = distance; + + /* Reset access-list configuration. */ + if (odistance->access_list) { + free(odistance->access_list); + odistance->access_list = NULL; + } + if (access_list_str) + odistance->access_list = strdup(access_list_str); + + return CMD_SUCCESS; +} + +int ospf_distance_unset(struct vty *vty, struct ospf *ospf, + const char *distance_str, const char *ip_str, + char const *access_list_str) +{ + int ret; + struct prefix_ipv4 p; + struct route_node *rn; + struct ospf_distance *odistance; + + ret = str2prefix_ipv4(ip_str, &p); + if (ret == 0) { + vty_out(vty, "Malformed prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + rn = route_node_lookup(ospf->distance_table, (struct prefix *)&p); + if (!rn) { + vty_out(vty, "Can't find specified prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + odistance = rn->info; + + if (odistance->access_list) + free(odistance->access_list); + ospf_distance_free(odistance); + + rn->info = NULL; + route_unlock_node(rn); + route_unlock_node(rn); + + return CMD_SUCCESS; +} + +void ospf_distance_reset(struct ospf *ospf) +{ + struct route_node *rn; + struct ospf_distance *odistance; + + for (rn = route_top(ospf->distance_table); rn; rn = route_next(rn)) { + odistance = rn->info; + if (!odistance) + continue; + + if (odistance->access_list) + free(odistance->access_list); + ospf_distance_free(odistance); + rn->info = NULL; + route_unlock_node(rn); + } +} + +uint8_t ospf_distance_apply(struct ospf *ospf, struct prefix_ipv4 *p, + struct ospf_route * or) +{ + + if (ospf == NULL) + return 0; + + if (ospf->distance_intra && or->path_type == OSPF_PATH_INTRA_AREA) + return ospf->distance_intra; + + if (ospf->distance_inter && or->path_type == OSPF_PATH_INTER_AREA) + return ospf->distance_inter; + + if (ospf->distance_external + && (or->path_type == OSPF_PATH_TYPE1_EXTERNAL || + or->path_type == OSPF_PATH_TYPE2_EXTERNAL)) + return ospf->distance_external; + + if (ospf->distance_all) + return ospf->distance_all; + + return 0; +} + +void ospf_zebra_vrf_register(struct ospf *ospf) +{ + if (!zclient || zclient->sock < 0 || !ospf) + return; + + if (ospf->vrf_id != VRF_UNKNOWN) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: Register VRF %s id %u", __func__, + ospf_vrf_id_to_name(ospf->vrf_id), + ospf->vrf_id); + zclient_send_reg_requests(zclient, ospf->vrf_id); + } +} + +void ospf_zebra_vrf_deregister(struct ospf *ospf) +{ + if (!zclient || zclient->sock < 0 || !ospf) + return; + + if (ospf->vrf_id != VRF_DEFAULT && ospf->vrf_id != VRF_UNKNOWN) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: De-Register VRF %s id %u to Zebra.", + __func__, ospf_vrf_id_to_name(ospf->vrf_id), + ospf->vrf_id); + /* Deregister for router-id, interfaces, + * redistributed routes. */ + zclient_send_dereg_requests(zclient, ospf->vrf_id); + } +} + +/* Label Manager Functions */ + +/** + * Check if Label Manager is Ready or not. + * + * @return True if Label Manager is ready, False otherwise + */ +bool ospf_zebra_label_manager_ready(void) +{ + return (zclient_sync->sock > 0); +} + +/** + * Request Label Range to the Label Manager. + * + * @param base base label of the label range to request + * @param chunk_size size of the label range to request + * + * @return 0 on success, -1 on failure + */ +int ospf_zebra_request_label_range(uint32_t base, uint32_t chunk_size) +{ + int ret; + uint32_t start, end; + + if (zclient_sync->sock < 0) + return -1; + + ret = lm_get_label_chunk(zclient_sync, 0, base, chunk_size, &start, + &end); + if (ret < 0) { + zlog_warn("%s: error getting label range!", __func__); + return -1; + } + + return 0; +} + +/** + * Release Label Range to the Label Manager. + * + * @param start start of label range to release + * @param end end of label range to release + * + * @return 0 on success, -1 otherwise + */ +int ospf_zebra_release_label_range(uint32_t start, uint32_t end) +{ + int ret; + + if (zclient_sync->sock < 0) + return -1; + + ret = lm_release_label_chunk(zclient_sync, start, end); + if (ret < 0) { + zlog_warn("%s: error releasing label range!", __func__); + return -1; + } + + return 0; +} + +/** + * Connect to the Label Manager. + * + * @return 0 on success, -1 otherwise + */ +int ospf_zebra_label_manager_connect(void) +{ + /* Connect to label manager. */ + if (zclient_socket_connect(zclient_sync) < 0) { + zlog_warn("%s: failed connecting synchronous zclient!", + __func__); + return -1; + } + /* make socket non-blocking */ + set_nonblocking(zclient_sync->sock); + + /* Send hello to notify zebra this is a synchronous client */ + if (zclient_send_hello(zclient_sync) == ZCLIENT_SEND_FAILURE) { + zlog_warn("%s: failed sending hello for synchronous zclient!", + __func__); + close(zclient_sync->sock); + zclient_sync->sock = -1; + return -1; + } + + /* Connect to label manager */ + if (lm_label_manager_connect(zclient_sync, 0) != 0) { + zlog_warn("%s: failed connecting to label manager!", __func__); + if (zclient_sync->sock > 0) { + close(zclient_sync->sock); + zclient_sync->sock = -1; + } + return -1; + } + + osr_debug("SR (%s): Successfully connected to the Label Manager", + __func__); + + return 0; +} + +static void ospf_zebra_connected(struct zclient *zclient) +{ + struct ospf *ospf; + struct listnode *node; + + /* Send the client registration */ + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); + + zclient_send_reg_requests(zclient, VRF_DEFAULT); + + /* Activate graceful restart if configured. */ + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->gr_info.restart_support) + continue; + (void)ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period); + } +} + +/* + * opaque messages between processes + */ +static int ospf_opaque_msg_handler(ZAPI_CALLBACK_ARGS) +{ + struct stream *s; + struct zapi_opaque_msg info; + struct ldp_igp_sync_if_state state; + struct ldp_igp_sync_announce announce; + struct zapi_opaque_reg_info dst; + int ret = 0; + + s = zclient->ibuf; + + if (zclient_opaque_decode(s, &info) != 0) + return -1; + + switch (info.type) { + case LINK_STATE_SYNC: + dst.proto = info.src_proto; + dst.instance = info.src_instance; + dst.session_id = info.src_session_id; + dst.type = LINK_STATE_SYNC; + ret = ospf_te_sync_ted(dst); + break; + case LDP_IGP_SYNC_IF_STATE_UPDATE: + STREAM_GET(&state, s, sizeof(state)); + ret = ospf_ldp_sync_state_update(state); + break; + case LDP_IGP_SYNC_ANNOUNCE_UPDATE: + STREAM_GET(&announce, s, sizeof(announce)); + ret = ospf_ldp_sync_announce_update(announce); + break; + default: + break; + } + +stream_failure: + + return ret; +} + +static int ospf_zebra_client_close_notify(ZAPI_CALLBACK_ARGS) +{ + int ret = 0; + + struct zapi_client_close_info info; + + if (zapi_client_close_notify_decode(zclient->ibuf, &info) < 0) + return -1; + + ospf_ldp_sync_handle_client_close(&info); + + return ret; +} + +static zclient_handler *const ospf_handlers[] = { + [ZEBRA_ROUTER_ID_UPDATE] = ospf_router_id_update_zebra, + [ZEBRA_INTERFACE_ADDRESS_ADD] = ospf_interface_address_add, + [ZEBRA_INTERFACE_ADDRESS_DELETE] = ospf_interface_address_delete, + [ZEBRA_INTERFACE_LINK_PARAMS] = ospf_interface_link_params, + + [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = ospf_zebra_read_route, + [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = ospf_zebra_read_route, + [ZEBRA_NEXTHOP_UPDATE] = ospf_zebra_import_check_update, + + [ZEBRA_OPAQUE_MESSAGE] = ospf_opaque_msg_handler, + + [ZEBRA_CLIENT_CLOSE_NOTIFY] = ospf_zebra_client_close_notify, +}; + +void ospf_zebra_init(struct event_loop *master, unsigned short instance) +{ + /* Allocate zebra structure. */ + zclient = zclient_new(master, &zclient_options_default, ospf_handlers, + array_size(ospf_handlers)); + zclient_init(zclient, ZEBRA_ROUTE_OSPF, instance, &ospfd_privs); + zclient->zebra_connected = ospf_zebra_connected; + + /* Initialize special zclient for synchronous message exchanges. */ + struct zclient_options options = zclient_options_default; + options.synchronous = true; + zclient_sync = zclient_new(master, &options, NULL, 0); + zclient_sync->sock = -1; + zclient_sync->redist_default = ZEBRA_ROUTE_OSPF; + zclient_sync->instance = instance; + /* + * session_id must be different from default value (0) to distinguish + * the asynchronous socket from the synchronous one + */ + zclient_sync->session_id = 1; + zclient_sync->privs = &ospfd_privs; + + access_list_add_hook(ospf_filter_update); + access_list_delete_hook(ospf_filter_update); + prefix_list_add_hook(ospf_prefix_list_update); + prefix_list_delete_hook(ospf_prefix_list_update); +} + +void ospf_zebra_send_arp(const struct interface *ifp, const struct prefix *p) +{ + zclient_send_neigh_discovery_req(zclient, ifp, p); +} diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h new file mode 100644 index 0000000..86a5678 --- /dev/null +++ b/ospfd/ospf_zebra.h @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zebra connect library for OSPFd + * Copyright (C) 1997, 98, 99, 2000 Kunihiro Ishiguro, Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPF_ZEBRA_H +#define _ZEBRA_OSPF_ZEBRA_H + +#include "vty.h" +#include "hook.h" + +#define EXTERNAL_METRIC_TYPE_1 0 +#define EXTERNAL_METRIC_TYPE_2 1 + +#define DEFAULT_ROUTE ZEBRA_ROUTE_MAX +#define DEFAULT_ROUTE_TYPE(T) ((T) == DEFAULT_ROUTE) + +/* OSPF distance. */ +struct ospf_distance { + /* Distance value for the IP source prefix. */ + uint8_t distance; + + /* Name of the access-list to be matched. */ + char *access_list; +}; + +/* Prototypes */ +struct ospf_route; +extern void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *, + struct ospf_route *); +extern void ospf_zebra_delete(struct ospf *ospf, struct prefix_ipv4 *, + struct ospf_route *); + +extern void ospf_zebra_add_discard(struct ospf *ospf, struct prefix_ipv4 *); +extern void ospf_zebra_delete_discard(struct ospf *ospf, struct prefix_ipv4 *); + +extern int ospf_redistribute_check(struct ospf *, struct external_info *, + int *); +extern int ospf_distribute_check_connected(struct ospf *, + struct external_info *); +extern void ospf_distribute_list_update(struct ospf *, int, unsigned short); + +extern int ospf_is_type_redistributed(struct ospf *, int, unsigned short); +extern void ospf_distance_reset(struct ospf *); +extern uint8_t ospf_distance_apply(struct ospf *ospf, struct prefix_ipv4 *, + struct ospf_route *); +extern struct ospf_external *ospf_external_lookup(struct ospf *, uint8_t, + unsigned short); +extern struct ospf_external *ospf_external_add(struct ospf *, uint8_t, + unsigned short); + +struct sr_prefix; +struct sr_nhlfe; +extern void ospf_zebra_update_prefix_sid(const struct sr_prefix *srp); +extern void ospf_zebra_delete_prefix_sid(const struct sr_prefix *srp); +extern void ospf_zebra_send_adjacency_sid(int cmd, struct sr_nhlfe nhlfe); + +extern void ospf_external_del(struct ospf *, uint8_t, unsigned short); +extern struct ospf_redist *ospf_redist_lookup(struct ospf *, uint8_t, + unsigned short); +extern struct ospf_redist *ospf_redist_add(struct ospf *, uint8_t, + unsigned short); +extern void ospf_redist_del(struct ospf *, uint8_t, unsigned short); + +extern int ospf_redistribute_update(struct ospf *, struct ospf_redist *, int, + unsigned short, int, int); +extern int ospf_redistribute_set(struct ospf *, struct ospf_redist *, int, + unsigned short, int, int); +extern int ospf_redistribute_unset(struct ospf *, int, unsigned short); +extern int ospf_redistribute_default_set(struct ospf *, int, int, int); +extern void ospf_zebra_import_default_route(struct ospf *ospf, bool unreg); +extern int ospf_distribute_list_out_set(struct ospf *, int, const char *); +extern int ospf_distribute_list_out_unset(struct ospf *, int, const char *); +extern void ospf_routemap_set(struct ospf_redist *, const char *); +extern void ospf_routemap_unset(struct ospf_redist *); +extern int ospf_zebra_gr_enable(struct ospf *ospf, uint32_t stale_time); +extern int ospf_zebra_gr_disable(struct ospf *ospf); +extern int ospf_distance_set(struct vty *, struct ospf *, const char *, + const char *, const char *); +extern int ospf_distance_unset(struct vty *, struct ospf *, const char *, + const char *, const char *); +extern void ospf_zebra_init(struct event_loop *m, unsigned short instance); +extern void ospf_zebra_vrf_register(struct ospf *ospf); +extern void ospf_zebra_vrf_deregister(struct ospf *ospf); +bool ospf_external_default_routemap_apply_walk( + struct ospf *ospf, struct list *ext_list, + struct external_info *default_ei); +int ospf_external_info_apply_default_routemap(struct ospf *ospf, + struct external_info *ei, + struct external_info *default_ei); + +extern void ospf_zebra_send_arp(const struct interface *ifp, + const struct prefix *p); +bool ospf_zebra_label_manager_ready(void); +int ospf_zebra_label_manager_connect(void); +int ospf_zebra_request_label_range(uint32_t base, uint32_t chunk_size); +int ospf_zebra_release_label_range(uint32_t start, uint32_t end); +#endif /* _ZEBRA_OSPF_ZEBRA_H */ diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c new file mode 100644 index 0000000..9ed1d30 --- /dev/null +++ b/ospfd/ospfd.c @@ -0,0 +1,2423 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* OSPF version 2 daemon program. + * Copyright (C) 1999, 2000 Toshiaki Takada + */ + +#include + +#include "frrevent.h" +#include "vty.h" +#include "command.h" +#include "linklist.h" +#include "prefix.h" +#include "table.h" +#include "if.h" +#include "memory.h" +#include "stream.h" +#include "log.h" +#include "sockunion.h" /* for inet_aton () */ +#include "zclient.h" +#include "routemap.h" +#include "plist.h" +#include "sockopt.h" +#include "bfd.h" +#include "libfrr.h" +#include "defaults.h" +#include "lib_errors.h" +#include "ldp_sync.h" + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_bfd.h" +#include "ospfd/ospf_network.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_abr.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_ldp_sync.h" +#include "ospfd/ospf_gr.h" +#include "ospfd/ospf_apiserver.h" + + +DEFINE_QOBJ_TYPE(ospf); + +/* OSPF process wide configuration. */ +static struct ospf_master ospf_master; + +/* OSPF process wide configuration pointer to export. */ +struct ospf_master *om; + +unsigned short ospf_instance; + +extern struct zclient *zclient; +extern struct zclient *zclient_sync; + +/* OSPF config processing timer thread */ +struct event *t_ospf_cfg; + +static void ospf_remove_vls_through_area(struct ospf *, struct ospf_area *); +static void ospf_network_free(struct ospf *, struct ospf_network *); +static void ospf_area_free(struct ospf_area *); +static void ospf_network_run(struct prefix *, struct ospf_area *); +static void ospf_network_run_interface(struct ospf *, struct interface *, + struct prefix *, struct ospf_area *); +static void ospf_network_run_subnet(struct ospf *, struct connected *, + struct prefix *, struct ospf_area *); +static int ospf_network_match_iface(const struct connected *, + const struct prefix *); +static void ospf_finish_final(struct ospf *); + +/* API to clean refresh queues and LSAs */ +static void ospf_free_refresh_queue(struct ospf *ospf) +{ + for (int i = 0; i < OSPF_LSA_REFRESHER_SLOTS; i++) { + struct list *list = ospf->lsa_refresh_queue.qs[i]; + struct listnode *node, *nnode; + struct ospf_lsa *lsa; + + if (list) { + for (ALL_LIST_ELEMENTS(list, node, nnode, lsa)) { + listnode_delete(list, lsa); + lsa->refresh_list = -1; + ospf_lsa_unlock(&lsa); + } + list_delete(&list); + ospf->lsa_refresh_queue.qs[i] = NULL; + } + } +} +#define OSPF_EXTERNAL_LSA_ORIGINATE_DELAY 1 + +int p_spaces_compare_func(const struct p_space *a, const struct p_space *b) +{ + if (a->protected_resource->type == OSPF_TI_LFA_LINK_PROTECTION + && b->protected_resource->type == OSPF_TI_LFA_LINK_PROTECTION) + return (a->protected_resource->link->link_id.s_addr + - b->protected_resource->link->link_id.s_addr); + + if (a->protected_resource->type == OSPF_TI_LFA_NODE_PROTECTION + && b->protected_resource->type == OSPF_TI_LFA_NODE_PROTECTION) + return (a->protected_resource->router_id.s_addr + - b->protected_resource->router_id.s_addr); + + /* This should not happen */ + return 0; +} + +int q_spaces_compare_func(const struct q_space *a, const struct q_space *b) +{ + return (a->root->id.s_addr - b->root->id.s_addr); +} + +DECLARE_RBTREE_UNIQ(p_spaces, struct p_space, p_spaces_item, + p_spaces_compare_func); + +void ospf_process_refresh_data(struct ospf *ospf, bool reset) +{ + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + struct in_addr router_id, router_id_old; + struct ospf_interface *oi; + struct interface *ifp; + struct listnode *node, *nnode; + struct ospf_area *area; + bool rid_change = false; + + if (!ospf->oi_running) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "Router ospf not configured -- Router-ID update postponed"); + return; + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Router-ID[OLD:%pI4]: Update", + &ospf->router_id); + + router_id_old = ospf->router_id; + + /* Select the router ID based on these priorities: + 1. Statically assigned router ID is always the first choice. + 2. If there is no statically assigned router ID, then try to stick + with the most recent value, since changing router ID's is very + disruptive. + 3. Last choice: just go with whatever the zebra daemon recommends. + */ + if (ospf->router_id_static.s_addr != INADDR_ANY) + router_id = ospf->router_id_static; + else if (ospf->router_id.s_addr != INADDR_ANY) + router_id = ospf->router_id; + else + router_id = ospf->router_id_zebra; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Router-ID[OLD:%pI4]: Update to %pI4", + &ospf->router_id, &router_id); + + rid_change = !(IPV4_ADDR_SAME(&router_id_old, &router_id)); + if (rid_change || (reset)) { + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + /* Some nbrs are identified by router_id, these needs + * to be rebuilt. Possible optimization would be to do + * oi->nbr_self->router_id = router_id for + * !(virtual | ptop) links + */ + ospf_nbr_self_reset(oi, router_id); + + /* + * If the old router id was not set, but now it + * is and the interface is operative and the + * state is ISM_Down we should kick the state + * machine as that we processed the interfaces + * based upon the network statement( or intf config ) + * but could not start it at that time. + */ + if (if_is_operative(oi->ifp) && oi->state == ISM_Down + && router_id_old.s_addr == INADDR_ANY) + ospf_if_up(oi); + } + + /* Flush (inline) all the self originated LSAs */ + ospf_flush_self_originated_lsas_now(ospf); + + ospf->router_id = router_id; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Router-ID[NEW:%pI4]: Update", + &ospf->router_id); + + /* Flush (inline) all external LSAs which now match the new + router-id, + need to adjust the OSPF_LSA_SELF flag, so the flush doesn't + hit + asserts in ospf_refresher_unregister_lsa(). This step is + needed + because the current frr code does look-up for + self-originated LSAs + based on the self router-id alone but expects OSPF_LSA_SELF + to be + properly set */ + if (ospf->lsdb) { + struct route_node *rn; + struct ospf_lsa *lsa; + + LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) { + /* AdvRouter and Router ID is the same. */ + if (IPV4_ADDR_SAME(&lsa->data->adv_router, + &ospf->router_id) && rid_change) { + SET_FLAG(lsa->flags, + OSPF_LSA_SELF_CHECKED); + SET_FLAG(lsa->flags, OSPF_LSA_SELF); + ospf_lsa_flush_schedule(ospf, lsa); + } + /* The above flush will send immediately + * So discard the LSA to originate new + */ + ospf_discard_from_db(ospf, ospf->lsdb, lsa); + } + + LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa) + ospf_discard_from_db(ospf, ospf->lsdb, lsa); + + ospf_lsdb_delete_all(ospf->lsdb); + } + + /* Since the LSAs are deleted, need reset the aggr flag */ + ospf_unset_all_aggr_flag(ospf); + + /* Delete the LSDB */ + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) + ospf_area_lsdb_discard_delete(area); + + /* update router-lsa's for each area */ + ospf_router_lsa_update(ospf); + + /* update ospf_interface's */ + FOR_ALL_INTERFACES (vrf, ifp) { + if (reset) + ospf_if_reset(ifp); + else + ospf_if_update(ospf, ifp); + } + + ospf_external_lsa_rid_change(ospf); + +#ifdef SUPPORT_OSPF_API + ospf_apiserver_clients_notify_router_id_change(router_id); +#endif + } + + ospf->inst_shutdown = 0; +} + +void ospf_router_id_update(struct ospf *ospf) +{ + ospf_process_refresh_data(ospf, false); +} + +void ospf_process_reset(struct ospf *ospf) +{ + ospf_process_refresh_data(ospf, true); +} + +void ospf_neighbor_reset(struct ospf *ospf, struct in_addr nbr_id, + const char *nbr_str) +{ + struct route_node *rn; + struct ospf_neighbor *nbr; + struct ospf_interface *oi; + struct listnode *node; + + /* Clear only a particular nbr with nbr router id as nbr_id */ + if (nbr_str != NULL) { + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + nbr = ospf_nbr_lookup_by_routerid(oi->nbrs, &nbr_id); + if (nbr) + OSPF_NSM_EVENT_EXECUTE(nbr, NSM_KillNbr); + } + return; + } + + /* send Neighbor event KillNbr to all associated neighbors. */ + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) { + nbr = rn->info; + if (nbr && (nbr != oi->nbr_self)) + OSPF_NSM_EVENT_EXECUTE(nbr, NSM_KillNbr); + } + } +} + +/* For OSPF area sort by area id. */ +static int ospf_area_id_cmp(struct ospf_area *a1, struct ospf_area *a2) +{ + if (ntohl(a1->area_id.s_addr) > ntohl(a2->area_id.s_addr)) + return 1; + if (ntohl(a1->area_id.s_addr) < ntohl(a2->area_id.s_addr)) + return -1; + return 0; +} + +static void ospf_add(struct ospf *ospf) +{ + listnode_add(om->ospf, ospf); +} + +static void ospf_delete(struct ospf *ospf) +{ + listnode_delete(om->ospf, ospf); +} + +struct ospf *ospf_new_alloc(unsigned short instance, const char *name) +{ + int i; + struct vrf *vrf = NULL; + + struct ospf *new = XCALLOC(MTYPE_OSPF_TOP, sizeof(struct ospf)); + + new->instance = instance; + new->router_id.s_addr = htonl(0); + new->router_id_static.s_addr = htonl(0); + + vrf = vrf_lookup_by_name(name); + if (vrf) + new->vrf_id = vrf->vrf_id; + else + new->vrf_id = VRF_UNKNOWN; + + /* Freed in ospf_finish_final */ + new->name = XSTRDUP(MTYPE_OSPF_TOP, name); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: Create new ospf instance with vrf_name %s vrf_id %u", + __func__, name, new->vrf_id); + + if (vrf) + ospf_vrf_link(new, vrf); + + ospf_zebra_vrf_register(new); + + new->abr_type = OSPF_ABR_DEFAULT; + new->oiflist = list_new(); + new->vlinks = list_new(); + new->areas = list_new(); + new->areas->cmp = (int (*)(void *, void *))ospf_area_id_cmp; + new->networks = route_table_init(); + new->nbr_nbma = route_table_init(); + + new->lsdb = ospf_lsdb_new(); + + new->default_originate = DEFAULT_ORIGINATE_NONE; + + new->passive_interface_default = OSPF_IF_ACTIVE; + + new->new_external_route = route_table_init(); + new->old_external_route = route_table_init(); + new->external_lsas = route_table_init(); + + new->stub_router_startup_time = OSPF_STUB_ROUTER_UNCONFIGURED; + new->stub_router_shutdown_time = OSPF_STUB_ROUTER_UNCONFIGURED; + new->stub_router_admin_set = OSPF_STUB_ROUTER_ADMINISTRATIVE_UNSET; + + /* Distribute parameter init. */ + for (i = 0; i <= ZEBRA_ROUTE_MAX; i++) { + new->dtag[i] = 0; + } + new->default_metric = -1; + new->ref_bandwidth = OSPF_DEFAULT_REF_BANDWIDTH; + + /* LSA timers */ + new->min_ls_interval = OSPF_MIN_LS_INTERVAL; + new->min_ls_arrival = OSPF_MIN_LS_ARRIVAL; + + /* SPF timer value init. */ + new->spf_delay = OSPF_SPF_DELAY_DEFAULT; + new->spf_holdtime = OSPF_SPF_HOLDTIME_DEFAULT; + new->spf_max_holdtime = OSPF_SPF_MAX_HOLDTIME_DEFAULT; + new->spf_hold_multiplier = 1; + + /* MaxAge init. */ + new->maxage_delay = OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT; + new->maxage_lsa = route_table_init(); + new->t_maxage_walker = NULL; + event_add_timer(master, ospf_lsa_maxage_walker, new, + OSPF_LSA_MAXAGE_CHECK_INTERVAL, &new->t_maxage_walker); + + /* Max paths initialization */ + new->max_multipath = MULTIPATH_NUM; + + /* Distance table init. */ + new->distance_table = route_table_init(); + + new->lsa_refresh_queue.index = 0; + new->lsa_refresh_interval = OSPF_LSA_REFRESH_INTERVAL_DEFAULT; + new->lsa_refresh_timer = OSPF_LS_REFRESH_TIME; + new->t_lsa_refresher = NULL; + event_add_timer(master, ospf_lsa_refresh_walker, new, + new->lsa_refresh_interval, &new->t_lsa_refresher); + new->lsa_refresher_started = monotime(NULL); + + new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE + 1); + + new->t_read = NULL; + new->oi_write_q = list_new(); + new->write_oi_count = OSPF_WRITE_INTERFACE_COUNT_DEFAULT; + + new->proactive_arp = OSPF_PROACTIVE_ARP_DEFAULT; + + ospf_gr_helper_instance_init(new); + + ospf_asbr_external_aggregator_init(new); + + ospf_opaque_type11_lsa_init(new); + + QOBJ_REG(new, ospf); + + new->fd = -1; + new->intf_socket_enabled = true; + + new->recv_sock_bufsize = OSPF_DEFAULT_SOCK_BUFSIZE; + new->send_sock_bufsize = OSPF_DEFAULT_SOCK_BUFSIZE; + + return new; +} + +/* Allocate new ospf structure. */ +static struct ospf *ospf_new(unsigned short instance, const char *name) +{ + struct ospf *new; + + new = ospf_new_alloc(instance, name); + ospf_add(new); + + if (new->vrf_id == VRF_UNKNOWN) + return new; + + if ((ospf_sock_init(new)) < 0) { + flog_err(EC_LIB_SOCKET, + "%s: ospf_sock_init is unable to open a socket", + __func__); + return new; + } + + event_add_read(master, ospf_read, new, new->fd, &new->t_read); + + new->oi_running = 1; + ospf_router_id_update(new); + + /* + * Read from non-volatile memory whether this instance is performing a + * graceful restart or not. + */ + ospf_gr_nvm_read(new); + + new->fr_configured = false; + + return new; +} + +struct ospf *ospf_lookup_instance(unsigned short instance) +{ + struct ospf *ospf; + struct listnode *node, *nnode; + + if (listcount(om->ospf) == 0) + return NULL; + + for (ALL_LIST_ELEMENTS(om->ospf, node, nnode, ospf)) + if ((ospf->instance == 0 && instance == 0) + || (ospf->instance && instance + && ospf->instance == instance)) + return ospf; + + return NULL; +} + +static int ospf_is_ready(struct ospf *ospf) +{ + /* OSPF must be on and Router-ID must be configured. */ + if (!ospf || ospf->router_id.s_addr == INADDR_ANY) + return 0; + + return 1; +} + +struct ospf *ospf_lookup_by_inst_name(unsigned short instance, const char *name) +{ + struct ospf *ospf = NULL; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(om->ospf, node, nnode, ospf)) { + if ((ospf->instance == instance) + && ((ospf->name == NULL && name == NULL) + || (ospf->name && name + && strcmp(ospf->name, name) == 0))) + return ospf; + } + return NULL; +} + +struct ospf *ospf_lookup(unsigned short instance, const char *name) +{ + struct ospf *ospf; + + if (ospf_instance) { + ospf = ospf_lookup_instance(instance); + } else { + ospf = ospf_lookup_by_inst_name(instance, name); + } + + return ospf; +} + +struct ospf *ospf_get(unsigned short instance, const char *name, bool *created) +{ + struct ospf *ospf; + + ospf = ospf_lookup(instance, name); + + *created = (ospf == NULL); + if (ospf == NULL) + ospf = ospf_new(instance, name); + + return ospf; +} + +struct ospf *ospf_lookup_by_vrf_id(vrf_id_t vrf_id) +{ + struct vrf *vrf = NULL; + + vrf = vrf_lookup_by_id(vrf_id); + if (!vrf) + return NULL; + return (vrf->info) ? (struct ospf *)vrf->info : NULL; +} + +uint32_t ospf_count_area_params(struct ospf *ospf) +{ + struct vrf *vrf; + struct interface *ifp; + uint32_t count = 0; + + if (ospf->vrf_id != VRF_UNKNOWN) { + vrf = vrf_lookup_by_id(ospf->vrf_id); + + FOR_ALL_INTERFACES (vrf, ifp) { + count += ospf_if_count_area_params(ifp); + } + } + + return count; +} + +/* It should only be used when processing incoming info update from zebra. + * Other situations, it is not sufficient to lookup the ospf instance by + * vrf_name only without using the instance number. + */ +static struct ospf *ospf_lookup_by_name(const char *vrf_name) +{ + struct ospf *ospf = NULL; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(om->ospf, node, nnode, ospf)) + if ((ospf->name == NULL && vrf_name == NULL) + || (ospf->name && vrf_name + && strcmp(ospf->name, vrf_name) == 0)) + return ospf; + return NULL; +} + +/* Handle the second half of deferred shutdown. This is called either + * from the deferred-shutdown timer thread, or directly through + * ospf_deferred_shutdown_check. + * + * Function is to cleanup G-R state, if required then call ospf_finish_final + * to complete shutdown of this ospf instance. Possibly exit if the + * whole process is being shutdown and this was the last OSPF instance. + */ +static void ospf_deferred_shutdown_finish(struct ospf *ospf) +{ + ospf->stub_router_shutdown_time = OSPF_STUB_ROUTER_UNCONFIGURED; + EVENT_OFF(ospf->t_deferred_shutdown); + + ospf_finish_final(ospf); + + /* *ospf is now invalid */ + + /* ospfd being shut-down? If so, was this the last ospf instance? */ + if (CHECK_FLAG(om->options, OSPF_MASTER_SHUTDOWN) + && (listcount(om->ospf) == 0)) { + frr_fini(); + exit(0); + } + + return; +} + +/* Timer thread for G-R */ +static void ospf_deferred_shutdown_timer(struct event *t) +{ + struct ospf *ospf = EVENT_ARG(t); + + ospf_deferred_shutdown_finish(ospf); +} + +/* Check whether deferred-shutdown must be scheduled, otherwise call + * down directly into second-half of instance shutdown. + */ +static void ospf_deferred_shutdown_check(struct ospf *ospf) +{ + unsigned long timeout; + struct listnode *ln; + struct ospf_area *area; + + /* deferred shutdown already running? */ + if (ospf->t_deferred_shutdown) + return; + + /* Should we try push out max-metric LSAs? */ + if (ospf->stub_router_shutdown_time != OSPF_STUB_ROUTER_UNCONFIGURED) { + for (ALL_LIST_ELEMENTS_RO(ospf->areas, ln, area)) { + SET_FLAG(area->stub_router_state, + OSPF_AREA_ADMIN_STUB_ROUTED); + + if (!CHECK_FLAG(area->stub_router_state, + OSPF_AREA_IS_STUB_ROUTED)) + ospf_router_lsa_update_area(area); + } + timeout = ospf->stub_router_shutdown_time; + } else { + /* No timer needed */ + ospf_deferred_shutdown_finish(ospf); + return; + } + + OSPF_TIMER_ON(ospf->t_deferred_shutdown, ospf_deferred_shutdown_timer, + timeout); + return; +} + +/* Shut down the entire process */ +void ospf_terminate(void) +{ + struct ospf *ospf; + struct listnode *node, *nnode; + + /* shutdown already in progress */ + if (CHECK_FLAG(om->options, OSPF_MASTER_SHUTDOWN)) + return; + + SET_FLAG(om->options, OSPF_MASTER_SHUTDOWN); + + /* Skip some steps if OSPF not actually running */ + if (listcount(om->ospf) == 0) + goto done; + + for (ALL_LIST_ELEMENTS(om->ospf, node, nnode, ospf)) + ospf_finish(ospf); + + /* Cleanup GR */ + ospf_gr_helper_stop(); + + /* Cleanup route maps */ + route_map_finish(); + + /* reverse prefix_list_init */ + prefix_list_add_hook(NULL); + prefix_list_delete_hook(NULL); + prefix_list_reset(); + + /* Cleanup vrf info */ + ospf_vrf_terminate(); + + /* Deliberately go back up, hopefully to thread scheduler, as + * One or more ospf_finish()'s may have deferred shutdown to a timer + * thread + */ + zclient_stop(zclient); + zclient_free(zclient); + zclient_stop(zclient_sync); + zclient_free(zclient_sync); + +done: + frr_fini(); +} + +void ospf_finish(struct ospf *ospf) +{ + /* let deferred shutdown decide */ + ospf_deferred_shutdown_check(ospf); + + /* if ospf_deferred_shutdown returns, then ospf_finish_final is + * deferred to expiry of G-S timer thread. Return back up, hopefully + * to thread scheduler. + */ + return; +} + +/* Final cleanup of ospf instance */ +static void ospf_finish_final(struct ospf *ospf) +{ + struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id); + struct route_node *rn; + struct ospf_nbr_nbma *nbr_nbma; + struct ospf_lsa *lsa; + struct ospf_interface *oi; + struct ospf_area *area; + struct ospf_vl_data *vl_data; + struct listnode *node, *nnode; + struct ospf_redist *red; + int i; + + QOBJ_UNREG(ospf); + + ospf_opaque_type11_lsa_term(ospf); + + ospf_opaque_finish(); + + if (!ospf->gr_info.prepare_in_progress) + ospf_flush_self_originated_lsas_now(ospf); + XFREE(MTYPE_TMP, ospf->gr_info.exit_reason); + + /* Unregister redistribution */ + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { + struct list *red_list; + + red_list = ospf->redist[i]; + if (!red_list) + continue; + + for (ALL_LIST_ELEMENTS(red_list, node, nnode, red)) { + ospf_redistribute_unset(ospf, i, red->instance); + ospf_redist_del(ospf, i, red->instance); + } + } + red = ospf_redist_lookup(ospf, DEFAULT_ROUTE, 0); + if (red) { + ospf_routemap_unset(red); + ospf_redist_del(ospf, DEFAULT_ROUTE, 0); + ospf_redistribute_default_set(ospf, DEFAULT_ORIGINATE_NONE, 0, 0); + } + + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) + ospf_remove_vls_through_area(ospf, area); + + for (ALL_LIST_ELEMENTS(ospf->vlinks, node, nnode, vl_data)) + ospf_vl_delete(ospf, vl_data); + + list_delete(&ospf->vlinks); + + /* shutdown LDP-Sync */ + if (ospf->vrf_id == VRF_DEFAULT) + ospf_ldp_sync_gbl_exit(ospf, true); + + /* Reset interface. */ + for (ALL_LIST_ELEMENTS(ospf->oiflist, node, nnode, oi)) + ospf_if_free(oi); + list_delete(&ospf->oiflist); + ospf->oi_running = 0; + + /* De-Register VRF */ + ospf_zebra_vrf_deregister(ospf); + + /* Clear static neighbors */ + for (rn = route_top(ospf->nbr_nbma); rn; rn = route_next(rn)) + if ((nbr_nbma = rn->info)) { + EVENT_OFF(nbr_nbma->t_poll); + + if (nbr_nbma->nbr) { + nbr_nbma->nbr->nbr_nbma = NULL; + nbr_nbma->nbr = NULL; + } + + if (nbr_nbma->oi) { + listnode_delete(nbr_nbma->oi->nbr_nbma, + nbr_nbma); + nbr_nbma->oi = NULL; + } + + XFREE(MTYPE_OSPF_NEIGHBOR_STATIC, nbr_nbma); + } + + route_table_finish(ospf->nbr_nbma); + + /* Clear networks and Areas. */ + for (rn = route_top(ospf->networks); rn; rn = route_next(rn)) { + struct ospf_network *network; + + if ((network = rn->info) != NULL) { + ospf_network_free(ospf, network); + rn->info = NULL; + route_unlock_node(rn); + } + } + route_table_finish(ospf->networks); + + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + listnode_delete(ospf->areas, area); + ospf_area_free(area); + } + + LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa) + ospf_discard_from_db(ospf, ospf->lsdb, lsa); + LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa) + ospf_discard_from_db(ospf, ospf->lsdb, lsa); + + ospf_lsdb_delete_all(ospf->lsdb); + ospf_lsdb_free(ospf->lsdb); + + for (rn = route_top(ospf->maxage_lsa); rn; rn = route_next(rn)) { + if ((lsa = rn->info) != NULL) { + ospf_lsa_unlock(&lsa); + rn->info = NULL; + route_unlock_node(rn); + } + } + route_table_finish(ospf->maxage_lsa); + + if (ospf->old_table) + ospf_route_table_free(ospf->old_table); + if (ospf->new_table) { + if (!ospf->gr_info.prepare_in_progress) + ospf_route_delete(ospf, ospf->new_table); + ospf_route_table_free(ospf->new_table); + } + if (ospf->oall_rtrs) + ospf_rtrs_free(ospf->oall_rtrs); + if (ospf->all_rtrs) + ospf_rtrs_free(ospf->all_rtrs); + if (ospf->old_rtrs) + ospf_rtrs_free(ospf->old_rtrs); + if (ospf->new_rtrs) + ospf_rtrs_free(ospf->new_rtrs); + if (ospf->new_external_route) { + if (!ospf->gr_info.prepare_in_progress) + ospf_route_delete(ospf, ospf->new_external_route); + ospf_route_table_free(ospf->new_external_route); + } + if (ospf->old_external_route) { + if (!ospf->gr_info.prepare_in_progress) + ospf_route_delete(ospf, ospf->old_external_route); + ospf_route_table_free(ospf->old_external_route); + } + if (ospf->external_lsas) { + ospf_ase_external_lsas_finish(ospf->external_lsas); + } + + for (i = ZEBRA_ROUTE_SYSTEM; i <= ZEBRA_ROUTE_MAX; i++) { + struct list *ext_list; + struct ospf_external *ext; + + ext_list = ospf->external[i]; + if (!ext_list) + continue; + + for (ALL_LIST_ELEMENTS(ext_list, node, nnode, ext)) { + if (ext->external_info) + for (rn = route_top(ext->external_info); rn; + rn = route_next(rn)) { + if (rn->info == NULL) + continue; + + XFREE(MTYPE_OSPF_EXTERNAL_INFO, + rn->info); + rn->info = NULL; + route_unlock_node(rn); + } + + ospf_external_del(ospf, i, ext->instance); + } + } + + ospf_distance_reset(ospf); + route_table_finish(ospf->distance_table); + + /* Release extrenal Aggregator table */ + for (rn = route_top(ospf->rt_aggr_tbl); rn; rn = route_next(rn)) { + struct ospf_external_aggr_rt *aggr; + + aggr = rn->info; + + if (aggr) { + ospf_external_aggregator_free(aggr); + rn->info = NULL; + route_unlock_node(rn); + } + } + + /* Cancel all timers. */ + EVENT_OFF(ospf->t_read); + EVENT_OFF(ospf->t_write); + EVENT_OFF(ospf->t_spf_calc); + EVENT_OFF(ospf->t_ase_calc); + EVENT_OFF(ospf->t_maxage); + EVENT_OFF(ospf->t_maxage_walker); + EVENT_OFF(ospf->t_abr_task); + EVENT_OFF(ospf->t_abr_fr); + EVENT_OFF(ospf->t_asbr_check); + EVENT_OFF(ospf->t_asbr_redist_update); + EVENT_OFF(ospf->t_distribute_update); + EVENT_OFF(ospf->t_lsa_refresher); + EVENT_OFF(ospf->t_opaque_lsa_self); + EVENT_OFF(ospf->t_sr_update); + EVENT_OFF(ospf->t_default_routemap_timer); + EVENT_OFF(ospf->t_external_aggr); + EVENT_OFF(ospf->gr_info.t_grace_period); + + route_table_finish(ospf->rt_aggr_tbl); + + ospf_free_refresh_queue(ospf); + + list_delete(&ospf->areas); + list_delete(&ospf->oi_write_q); + + /* Reset GR helper data structers */ + ospf_gr_helper_instance_stop(ospf); + + close(ospf->fd); + stream_free(ospf->ibuf); + ospf->fd = -1; + ospf->max_multipath = MULTIPATH_NUM; + ospf_delete(ospf); + + if (vrf) + ospf_vrf_unlink(ospf, vrf); + + XFREE(MTYPE_OSPF_TOP, ospf->name); + XFREE(MTYPE_OSPF_TOP, ospf); +} + +static void ospf_range_table_node_destroy(route_table_delegate_t *delegate, + struct route_table *table, struct route_node *node) +{ + XFREE(MTYPE_OSPF_AREA_RANGE, node->info); + XFREE(MTYPE_ROUTE_NODE, node); +} + +route_table_delegate_t ospf_range_table_delegate = {.create_node = route_node_create, + .destroy_node = ospf_range_table_node_destroy}; + +/* allocate new OSPF Area object */ +struct ospf_area *ospf_area_new(struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *new; + + /* Allocate new config_network. */ + new = XCALLOC(MTYPE_OSPF_AREA, sizeof(struct ospf_area)); + + new->ospf = ospf; + + new->area_id = area_id; + new->area_id_fmt = OSPF_AREA_ID_FMT_DOTTEDQUAD; + + new->external_routing = OSPF_AREA_DEFAULT; + new->default_cost = 1; + new->auth_type = OSPF_AUTH_NULL; + + /* New LSDB init. */ + new->lsdb = ospf_lsdb_new(); + + /* Self-originated LSAs initialize. */ + new->router_lsa_self = NULL; + + /* Initialize FR field */ + new->fr_info.enabled = false; + new->fr_info.configured = false; + new->fr_info.state_changed = false; + new->fr_info.router_lsas_recv_dc_bit = 0; + new->fr_info.indication_lsa_self = NULL; + new->fr_info.area_ind_lsa_recvd = false; + new->fr_info.area_dc_clear = false; + + ospf_opaque_type10_lsa_init(new); + + new->oiflist = list_new(); + new->ranges = route_table_init_with_delegate(&ospf_range_table_delegate); + new->nssa_ranges = route_table_init_with_delegate(&ospf_range_table_delegate); + + if (area_id.s_addr == OSPF_AREA_BACKBONE) + ospf->backbone = new; + + return new; +} + +void ospf_area_lsdb_discard_delete(struct ospf_area *area) +{ + struct route_node *rn; + struct ospf_lsa *lsa; + + LSDB_LOOP (ROUTER_LSDB(area), rn, lsa) + ospf_discard_from_db(area->ospf, area->lsdb, lsa); + LSDB_LOOP (NETWORK_LSDB(area), rn, lsa) + ospf_discard_from_db(area->ospf, area->lsdb, lsa); + LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa) + ospf_discard_from_db(area->ospf, area->lsdb, lsa); + LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa) + ospf_discard_from_db(area->ospf, area->lsdb, lsa); + + LSDB_LOOP (NSSA_LSDB(area), rn, lsa) + ospf_discard_from_db(area->ospf, area->lsdb, lsa); + LSDB_LOOP (OPAQUE_AREA_LSDB(area), rn, lsa) + ospf_discard_from_db(area->ospf, area->lsdb, lsa); + LSDB_LOOP (OPAQUE_LINK_LSDB(area), rn, lsa) + ospf_discard_from_db(area->ospf, area->lsdb, lsa); + + ospf_lsdb_delete_all(area->lsdb); +} + +static void ospf_area_free(struct ospf_area *area) +{ + ospf_opaque_type10_lsa_term(area); + + /* Free LSDBs. */ + ospf_area_lsdb_discard_delete(area); + + ospf_lsdb_free(area->lsdb); + + ospf_lsa_unlock(&area->router_lsa_self); + + route_table_finish(area->ranges); + route_table_finish(area->nssa_ranges); + list_delete(&area->oiflist); + + if (EXPORT_NAME(area)) + free(EXPORT_NAME(area)); + + if (IMPORT_NAME(area)) + free(IMPORT_NAME(area)); + + /* Cancel timer. */ + EVENT_OFF(area->t_stub_router); + EVENT_OFF(area->t_opaque_lsa_self); + + if (OSPF_IS_AREA_BACKBONE(area)) + area->ospf->backbone = NULL; + + XFREE(MTYPE_OSPF_AREA, area); +} + +void ospf_area_check_free(struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area && listcount(area->oiflist) == 0 && + area->ranges->top == NULL && area->nssa_ranges->top == NULL && + !ospf_vl_count(ospf, area) && + area->shortcut_configured == OSPF_SHORTCUT_DEFAULT && + area->external_routing == OSPF_AREA_DEFAULT && + area->no_summary == 0 && area->default_cost == 1 && + EXPORT_NAME(area) == NULL && IMPORT_NAME(area) == NULL && + area->auth_type == OSPF_AUTH_NULL) { + listnode_delete(ospf->areas, area); + ospf_area_free(area); + } +} + +struct ospf_area *ospf_area_get(struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (!area) { + area = ospf_area_new(ospf, area_id); + listnode_add_sort(ospf->areas, area); + ospf_check_abr_status(ospf); + if (ospf->stub_router_admin_set + == OSPF_STUB_ROUTER_ADMINISTRATIVE_SET) { + SET_FLAG(area->stub_router_state, + OSPF_AREA_ADMIN_STUB_ROUTED); + } + } + + return area; +} + +struct ospf_area *ospf_area_lookup_by_area_id(struct ospf *ospf, + struct in_addr area_id) +{ + struct ospf_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) + if (IPV4_ADDR_SAME(&area->area_id, &area_id)) + return area; + + return NULL; +} + +void ospf_area_add_if(struct ospf_area *area, struct ospf_interface *oi) +{ + listnode_add(area->oiflist, oi); +} + +void ospf_area_del_if(struct ospf_area *area, struct ospf_interface *oi) +{ + listnode_delete(area->oiflist, oi); +} + + +struct ospf_interface *add_ospf_interface(struct connected *co, + struct ospf_area *area) +{ + struct ospf_interface *oi; + + oi = ospf_if_new(area->ospf, co->ifp, co->address); + oi->connected = co; + + oi->area = area; + + oi->params = ospf_lookup_if_params(co->ifp, oi->address->u.prefix4); + oi->output_cost = ospf_if_get_output_cost(oi); + + /* Relate ospf interface to ospf instance. */ + oi->ospf = area->ospf; + + /* update network type as interface flag */ + /* If network type is specified previously, + skip network type setting. */ + oi->type = IF_DEF_PARAMS(co->ifp)->type; + oi->ptp_dmvpn = IF_DEF_PARAMS(co->ifp)->ptp_dmvpn; + oi->p2mp_delay_reflood = IF_DEF_PARAMS(co->ifp)->p2mp_delay_reflood; + + /* Add pseudo neighbor. */ + ospf_nbr_self_reset(oi, oi->ospf->router_id); + + ospf_area_add_if(oi->area, oi); + + /* if LDP-IGP Sync is configured globally inherit config */ + ospf_ldp_sync_if_init(oi); + + /* + * if router_id is not configured, don't bring up + * interfaces. + * ospf_router_id_update() will call ospf_if_update + * whenever r-id is configured instead. + */ + if ((area->ospf->router_id.s_addr != INADDR_ANY) + && if_is_operative(co->ifp)) + ospf_if_up(oi); + + /* + * RFC 3623 - Section 5 ("Unplanned Outages"): + * "The grace-LSAs are encapsulated in Link State Update Packets + * and sent out to all interfaces, even though the restarted + * router has no adjacencies and no knowledge of previous + * adjacencies". + */ + if (oi->ospf->gr_info.restart_in_progress && + oi->ospf->gr_info.reason == OSPF_GR_UNKNOWN_RESTART) + ospf_gr_unplanned_start_interface(oi); + + return oi; +} + +static void update_redistributed(struct ospf *ospf, int add_to_ospf) +{ + struct route_node *rn; + struct external_info *ei; + struct ospf_external *ext; + + if (ospf_is_type_redistributed(ospf, ZEBRA_ROUTE_CONNECT, 0)) { + ext = ospf_external_lookup(ospf, ZEBRA_ROUTE_CONNECT, 0); + if ((ext) && EXTERNAL_INFO(ext)) { + for (rn = route_top(EXTERNAL_INFO(ext)); rn; + rn = route_next(rn)) { + ei = rn->info; + if (ei == NULL) + continue; + + if (add_to_ospf) { + if (ospf_external_info_find_lsa(ospf, + &ei->p)) + if (!ospf_redistribute_check( + ospf, ei, NULL)) + ospf_external_lsa_flush( + ospf, ei->type, + &ei->p, + ei->ifindex /*, ei->nexthop */); + } else { + if (!ospf_external_info_find_lsa( + ospf, &ei->p)) + if (ospf_redistribute_check( + ospf, ei, NULL)) + ospf_external_lsa_originate( + ospf, ei); + } + } + } + } +} + +/* Config network statement related functions. */ +static struct ospf_network *ospf_network_new(struct in_addr area_id) +{ + struct ospf_network *new; + new = XCALLOC(MTYPE_OSPF_NETWORK, sizeof(struct ospf_network)); + + new->area_id = area_id; + new->area_id_fmt = OSPF_AREA_ID_FMT_DOTTEDQUAD; + + return new; +} + +static void ospf_network_free(struct ospf *ospf, struct ospf_network *network) +{ + ospf_area_check_free(ospf, network->area_id); + ospf_schedule_abr_task(ospf); + XFREE(MTYPE_OSPF_NETWORK, network); +} + +int ospf_network_set(struct ospf *ospf, struct prefix_ipv4 *p, + struct in_addr area_id, int df) +{ + struct ospf_network *network; + struct ospf_area *area; + struct route_node *rn; + + rn = route_node_get(ospf->networks, (struct prefix *)p); + if (rn->info) { + network = rn->info; + route_unlock_node(rn); + + if (IPV4_ADDR_SAME(&area_id, &network->area_id)) { + return 1; + } else { + /* There is already same network statement. */ + return 0; + } + } + + rn->info = network = ospf_network_new(area_id); + network->area_id_fmt = df; + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, df); + + /* Run network config now. */ + ospf_network_run((struct prefix *)p, area); + + /* Update connected redistribute. */ + update_redistributed(ospf, 1); /* interfaces possibly added */ + + ospf_area_check_free(ospf, area_id); + + return 1; +} + +int ospf_network_unset(struct ospf *ospf, struct prefix_ipv4 *p, + struct in_addr area_id) +{ + struct route_node *rn; + struct ospf_network *network; + struct listnode *node; + struct ospf_interface *oi; + struct list *ospf_oiflist = NULL; + + rn = route_node_lookup(ospf->networks, (struct prefix *)p); + if (rn == NULL) + return 0; + + network = rn->info; + route_unlock_node(rn); + if (!IPV4_ADDR_SAME(&area_id, &network->area_id)) + return 0; + + ospf_network_free(ospf, rn->info); + rn->info = NULL; + route_unlock_node(rn); /* initial reference */ + + ospf_oiflist = list_dup(ospf->oiflist); + /* Find interfaces that are not configured already. */ + for (ALL_LIST_ELEMENTS_RO(ospf_oiflist, node, oi)) { + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + continue; + + ospf_network_run_subnet(ospf, oi->connected, NULL, NULL); + } + + list_delete(&ospf_oiflist); + + /* Update connected redistribute. */ + update_redistributed(ospf, 0); /* interfaces possibly removed */ + ospf_area_check_free(ospf, area_id); + + return 1; +} + + +/* Ensure there's an OSPF instance, as "ip ospf area" enabled OSPF means + * there might not be any 'router ospf' config. + * + * Otherwise, doesn't do anything different to ospf_if_update for now + */ +void ospf_interface_area_set(struct ospf *ospf, struct interface *ifp) +{ + if (!ospf) + return; + + ospf_if_update(ospf, ifp); + /* if_update does a update_redistributed */ + + return; +} + +void ospf_interface_area_unset(struct ospf *ospf, struct interface *ifp) +{ + struct route_node *rn_oi; + + if (!ospf) + return; /* Ospf not ready yet */ + + /* Find interfaces that may need to be removed. */ + for (rn_oi = route_top(IF_OIFS(ifp)); rn_oi; + rn_oi = route_next(rn_oi)) { + struct ospf_interface *oi = NULL; + + if ((oi = rn_oi->info) == NULL) + continue; + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + continue; + + ospf_network_run_subnet(ospf, oi->connected, NULL, NULL); + } + + /* Update connected redistribute. */ + update_redistributed(ospf, 0); /* interfaces possibly removed */ +} + +/* Check whether interface matches given network + * returns: 1, true. 0, false + */ +static int ospf_network_match_iface(const struct connected *co, + const struct prefix *net) +{ + /* new approach: more elegant and conceptually clean */ + return prefix_match_network_statement(net, CONNECTED_PREFIX(co)); +} + +static void ospf_update_interface_area(struct connected *co, + struct ospf_area *area) +{ + struct ospf_interface *oi = ospf_if_table_lookup(co->ifp, co->address); + + /* nothing to be done case */ + if (oi && oi->area == area) { + return; + } + + if (oi) + ospf_if_free(oi); + + add_ospf_interface(co, area); +} + +/* Run OSPF for the given subnet, taking into account the following + * possible sources of area configuration, in the given order of preference: + * + * - Whether there is interface+address specific area configuration + * - Whether there is a default area for the interface + * - Whether there is an area given as a parameter. + * - If no specific network prefix/area is supplied, whether there's + * a matching network configured. + */ +static void ospf_network_run_subnet(struct ospf *ospf, struct connected *co, + struct prefix *p, + struct ospf_area *given_area) +{ + struct ospf_interface *oi; + struct ospf_if_params *params; + struct ospf_area *area = NULL; + struct route_node *rn; + int configed = 0; + + if (CHECK_FLAG(co->flags, ZEBRA_IFA_SECONDARY)) + return; + + if (co->address->family != AF_INET) + return; + + /* Try determine the appropriate area for this interface + address + * Start by checking interface config + */ + params = ospf_lookup_if_params(co->ifp, co->address->u.prefix4); + if (params && OSPF_IF_PARAM_CONFIGURED(params, if_area)) + area = ospf_area_get(ospf, params->if_area); + else { + params = IF_DEF_PARAMS(co->ifp); + if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) + area = ospf_area_get(ospf, params->if_area); + } + + /* If we've found an interface and/or addr specific area, then we're + * done + */ + if (area) { + ospf_update_interface_area(co, area); + return; + } + + /* Otherwise, only remaining possibility is a matching network statement + */ + if (p) { + assert(given_area != NULL); + + /* Which either was supplied as a parameter.. (e.g. cause a new + * network/area was just added).. + */ + if (p->family == co->address->family + && ospf_network_match_iface(co, p)) + ospf_update_interface_area(co, given_area); + + return; + } + + /* Else we have to search the existing network/area config to see + * if any match.. + */ + for (rn = route_top(ospf->networks); rn; rn = route_next(rn)) + if (rn->info != NULL && ospf_network_match_iface(co, &rn->p)) { + struct ospf_network *network = + (struct ospf_network *)rn->info; + area = ospf_area_get(ospf, network->area_id); + ospf_update_interface_area(co, area); + configed = 1; + } + + /* If the subnet isn't in any area, deconfigure */ + if (!configed && (oi = ospf_if_table_lookup(co->ifp, co->address))) + ospf_if_free(oi); +} + +static void ospf_network_run_interface(struct ospf *ospf, struct interface *ifp, + struct prefix *p, + struct ospf_area *given_area) +{ + struct listnode *cnode; + struct connected *co; + + if (memcmp(ifp->name, "VLINK", 5) == 0) + return; + + /* Network prefix without area is nonsensical */ + if (p) + assert(given_area != NULL); + + /* if interface prefix is match specified prefix, + then create socket and join multicast group. */ + for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, co)) + ospf_network_run_subnet(ospf, co, p, given_area); +} + +static void ospf_network_run(struct prefix *p, struct ospf_area *area) +{ + struct vrf *vrf = vrf_lookup_by_id(area->ospf->vrf_id); + struct interface *ifp; + + /* Schedule Router ID Update. */ + if (area->ospf->router_id.s_addr == INADDR_ANY) + ospf_router_id_update(area->ospf); + + /* Get target interface. */ + FOR_ALL_INTERFACES (vrf, ifp) + ospf_network_run_interface(area->ospf, ifp, p, area); +} + +void ospf_ls_upd_queue_empty(struct ospf_interface *oi) +{ + struct route_node *rn; + struct listnode *node, *nnode; + struct list *lst; + struct ospf_lsa *lsa; + + /* empty ls update queue */ + for (rn = route_top(oi->ls_upd_queue); rn; rn = route_next(rn)) + if ((lst = (struct list *)rn->info)) { + for (ALL_LIST_ELEMENTS(lst, node, nnode, lsa)) + ospf_lsa_unlock(&lsa); /* oi->ls_upd_queue */ + list_delete(&lst); + rn->info = NULL; + } + + /* remove update event */ + EVENT_OFF(oi->t_ls_upd_event); +} + +void ospf_if_update(struct ospf *ospf, struct interface *ifp) +{ + + if (!ospf) + return; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: interface %s vrf %s(%u) ospf vrf %s vrf_id %u router_id %pI4", + __func__, ifp->name, ifp->vrf->name, ifp->vrf->vrf_id, + ospf_vrf_id_to_name(ospf->vrf_id), ospf->vrf_id, + &ospf->router_id); + + /* OSPF must be ready. */ + if (!ospf_is_ready(ospf)) + return; + + ospf_network_run_interface(ospf, ifp, NULL, NULL); + + /* Update connected redistribute. */ + update_redistributed(ospf, 1); + +} + +void ospf_remove_vls_through_area(struct ospf *ospf, struct ospf_area *area) +{ + struct listnode *node, *nnode; + struct ospf_vl_data *vl_data; + + for (ALL_LIST_ELEMENTS(ospf->vlinks, node, nnode, vl_data)) + if (IPV4_ADDR_SAME(&vl_data->vl_area_id, &area->area_id)) + ospf_vl_delete(ospf, vl_data); +} + + +static const struct message ospf_area_type_msg[] = { + {OSPF_AREA_DEFAULT, "Default"}, + {OSPF_AREA_STUB, "Stub"}, + {OSPF_AREA_NSSA, "NSSA"}, + {0}}; + +static void ospf_area_type_set(struct ospf_area *area, int type) +{ + struct listnode *node; + struct ospf_interface *oi; + + if (area->external_routing == type) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Area[%pI4]: Types are the same, ignored.", + &area->area_id); + return; + } + + area->external_routing = type; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("Area[%pI4]: Configured as %s", + &area->area_id, + lookup_msg(ospf_area_type_msg, type, NULL)); + + switch (area->external_routing) { + case OSPF_AREA_DEFAULT: + for (ALL_LIST_ELEMENTS_RO(area->oiflist, node, oi)) + if (oi->nbr_self != NULL) { + UNSET_FLAG(oi->nbr_self->options, + OSPF_OPTION_NP); + SET_FLAG(oi->nbr_self->options, OSPF_OPTION_E); + } + break; + case OSPF_AREA_STUB: + for (ALL_LIST_ELEMENTS_RO(area->oiflist, node, oi)) + if (oi->nbr_self != NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "setting options on %s accordingly", + IF_NAME(oi)); + UNSET_FLAG(oi->nbr_self->options, + OSPF_OPTION_NP); + UNSET_FLAG(oi->nbr_self->options, + OSPF_OPTION_E); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("options set on %s: %x", + IF_NAME(oi), OPTIONS(oi)); + } + break; + case OSPF_AREA_NSSA: + for (ALL_LIST_ELEMENTS_RO(area->oiflist, node, oi)) + if (oi->nbr_self != NULL) { + zlog_debug( + "setting nssa options on %s accordingly", + IF_NAME(oi)); + UNSET_FLAG(oi->nbr_self->options, + OSPF_OPTION_E); + SET_FLAG(oi->nbr_self->options, OSPF_OPTION_NP); + zlog_debug("options set on %s: %x", IF_NAME(oi), + OPTIONS(oi)); + } + break; + default: + break; + } + + ospf_router_lsa_update_area(area); + ospf_schedule_abr_task(area->ospf); +} + +int ospf_area_shortcut_set(struct ospf *ospf, struct ospf_area *area, int mode) +{ + if (area->shortcut_configured == mode) + return 0; + + area->shortcut_configured = mode; + ospf_router_lsa_update_area(area); + ospf_schedule_abr_task(ospf); + + ospf_area_check_free(ospf, area->area_id); + + return 1; +} + +int ospf_area_shortcut_unset(struct ospf *ospf, struct ospf_area *area) +{ + area->shortcut_configured = OSPF_SHORTCUT_DEFAULT; + ospf_router_lsa_update_area(area); + ospf_area_check_free(ospf, area->area_id); + ospf_schedule_abr_task(ospf); + + return 1; +} + +static int ospf_area_vlink_count(struct ospf *ospf, struct ospf_area *area) +{ + struct ospf_vl_data *vl; + struct listnode *node; + int count = 0; + + for (ALL_LIST_ELEMENTS_RO(ospf->vlinks, node, vl)) + if (IPV4_ADDR_SAME(&vl->vl_area_id, &area->area_id)) + count++; + + return count; +} + +int ospf_area_display_format_set(struct ospf *ospf, struct ospf_area *area, + int df) +{ + area->area_id_fmt = df; + + return 1; +} + +int ospf_area_stub_set(struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_get(ospf, area_id); + if (ospf_area_vlink_count(ospf, area)) + return 0; + + if (area->external_routing != OSPF_AREA_STUB) + ospf_area_type_set(area, OSPF_AREA_STUB); + + return 1; +} + +int ospf_area_stub_unset(struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return 1; + + if (area->external_routing == OSPF_AREA_STUB) + ospf_area_type_set(area, OSPF_AREA_DEFAULT); + + ospf_area_check_free(ospf, area_id); + + return 1; +} + +int ospf_area_no_summary_set(struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_get(ospf, area_id); + area->no_summary = 1; + + return 1; +} + +int ospf_area_no_summary_unset(struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return 0; + + area->no_summary = 0; + ospf_area_check_free(ospf, area_id); + + return 1; +} + +int ospf_area_nssa_no_summary_set(struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_get(ospf, area_id); + if (ospf_area_vlink_count(ospf, area)) + return 0; + + if (area->external_routing != OSPF_AREA_NSSA) { + ospf_area_type_set(area, OSPF_AREA_NSSA); + ospf->anyNSSA++; + area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE; + } + + ospf_area_no_summary_set(ospf, area_id); + + return 1; +} + +int ospf_area_nssa_set(struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_get(ospf, area_id); + if (ospf_area_vlink_count(ospf, area)) + return 0; + + if (area->external_routing != OSPF_AREA_NSSA) { + ospf_area_type_set(area, OSPF_AREA_NSSA); + ospf->anyNSSA++; + + /* set NSSA area defaults */ + area->no_summary = 0; + area->suppress_fa = 0; + area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE; + area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; + area->NSSATranslatorStabilityInterval = + OSPF_NSSA_TRANS_STABLE_DEFAULT; + } + return 1; +} + +int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return 0; + + ospf->anyNSSA--; + /* set NSSA area defaults */ + area->no_summary = 0; + area->suppress_fa = 0; + area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE; + area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; + area->NSSATranslatorStabilityInterval = OSPF_NSSA_TRANS_STABLE_DEFAULT; + ospf_area_type_set(area, OSPF_AREA_DEFAULT); + ospf_area_check_free(ospf, area_id); + + return 1; +} + +int ospf_area_nssa_suppress_fa_set(struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return 0; + + area->suppress_fa = 1; + + return 1; +} + +int ospf_area_nssa_suppress_fa_unset(struct ospf *ospf, struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return 0; + + area->suppress_fa = 0; + + return 1; +} + +int ospf_area_nssa_translator_role_set(struct ospf *ospf, + struct in_addr area_id, int role) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return 0; + + if (role != area->NSSATranslatorRole) { + if ((area->NSSATranslatorRole == OSPF_NSSA_ROLE_ALWAYS) + || (role == OSPF_NSSA_ROLE_ALWAYS)) { + /* RFC 3101 3.1 + * if new role is OSPF_NSSA_ROLE_ALWAYS we need to set + * Nt bit, if the role was OSPF_NSSA_ROLE_ALWAYS we need + * to clear Nt bit + */ + area->NSSATranslatorRole = role; + ospf_router_lsa_update_area(area); + } else + area->NSSATranslatorRole = role; + } + + return 1; +} + +void ospf_area_nssa_default_originate_set(struct ospf *ospf, + struct in_addr area_id, int metric, + int metric_type) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return; + + if (!area->nssa_default_originate.enabled) { + area->nssa_default_originate.enabled = true; + if (++ospf->nssa_default_import_check.refcnt == 1) { + ospf->nssa_default_import_check.status = false; + ospf_zebra_import_default_route(ospf, false); + } + } + + area->nssa_default_originate.metric_value = metric; + area->nssa_default_originate.metric_type = metric_type; +} + +void ospf_area_nssa_default_originate_unset(struct ospf *ospf, + struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return; + + if (area->nssa_default_originate.enabled) { + area->nssa_default_originate.enabled = false; + if (--ospf->nssa_default_import_check.refcnt == 0) { + ospf->nssa_default_import_check.status = false; + ospf_zebra_import_default_route(ospf, true); + } + area->nssa_default_originate.metric_value = -1; + area->nssa_default_originate.metric_type = -1; + + if (!IS_OSPF_ABR(ospf)) + ospf_abr_nssa_type7_defaults(ospf); + } +} + +int ospf_area_export_list_set(struct ospf *ospf, struct ospf_area *area, + const char *list_name) +{ + struct access_list *list; + list = access_list_lookup(AFI_IP, list_name); + + EXPORT_LIST(area) = list; + + if (EXPORT_NAME(area)) + free(EXPORT_NAME(area)); + + EXPORT_NAME(area) = strdup(list_name); + ospf_schedule_abr_task(ospf); + + return 1; +} + +int ospf_area_export_list_unset(struct ospf *ospf, struct ospf_area *area) +{ + + EXPORT_LIST(area) = 0; + + if (EXPORT_NAME(area)) + free(EXPORT_NAME(area)); + + EXPORT_NAME(area) = NULL; + + ospf_area_check_free(ospf, area->area_id); + + ospf_schedule_abr_task(ospf); + + return 1; +} + +int ospf_area_import_list_set(struct ospf *ospf, struct ospf_area *area, + const char *name) +{ + struct access_list *list; + list = access_list_lookup(AFI_IP, name); + + IMPORT_LIST(area) = list; + + if (IMPORT_NAME(area)) + free(IMPORT_NAME(area)); + + IMPORT_NAME(area) = strdup(name); + ospf_schedule_abr_task(ospf); + + return 1; +} + +int ospf_area_import_list_unset(struct ospf *ospf, struct ospf_area *area) +{ + IMPORT_LIST(area) = 0; + + if (IMPORT_NAME(area)) + free(IMPORT_NAME(area)); + + IMPORT_NAME(area) = NULL; + ospf_area_check_free(ospf, area->area_id); + + ospf_schedule_abr_task(ospf); + + return 1; +} + +int ospf_timers_refresh_set(struct ospf *ospf, int interval) +{ + int time_left; + + if (ospf->lsa_refresh_interval == interval) + return 1; + + time_left = ospf->lsa_refresh_interval + - (monotime(NULL) - ospf->lsa_refresher_started); + + if (time_left > interval) { + EVENT_OFF(ospf->t_lsa_refresher); + event_add_timer(master, ospf_lsa_refresh_walker, ospf, interval, + &ospf->t_lsa_refresher); + } + ospf->lsa_refresh_interval = interval; + + return 1; +} + +int ospf_timers_refresh_unset(struct ospf *ospf) +{ + int time_left; + + time_left = ospf->lsa_refresh_interval + - (monotime(NULL) - ospf->lsa_refresher_started); + + if (time_left > OSPF_LSA_REFRESH_INTERVAL_DEFAULT) { + EVENT_OFF(ospf->t_lsa_refresher); + ospf->t_lsa_refresher = NULL; + event_add_timer(master, ospf_lsa_refresh_walker, ospf, + OSPF_LSA_REFRESH_INTERVAL_DEFAULT, + &ospf->t_lsa_refresher); + } + + ospf->lsa_refresh_interval = OSPF_LSA_REFRESH_INTERVAL_DEFAULT; + + return 1; +} + + +static struct ospf_nbr_nbma *ospf_nbr_nbma_new(void) +{ + struct ospf_nbr_nbma *nbr_nbma; + + nbr_nbma = XCALLOC(MTYPE_OSPF_NEIGHBOR_STATIC, + sizeof(struct ospf_nbr_nbma)); + + nbr_nbma->priority = OSPF_NEIGHBOR_PRIORITY_DEFAULT; + nbr_nbma->v_poll = OSPF_POLL_INTERVAL_DEFAULT; + + return nbr_nbma; +} + +static void ospf_nbr_nbma_free(struct ospf_nbr_nbma *nbr_nbma) +{ + XFREE(MTYPE_OSPF_NEIGHBOR_STATIC, nbr_nbma); +} + +static void ospf_nbr_nbma_delete(struct ospf *ospf, + struct ospf_nbr_nbma *nbr_nbma) +{ + struct route_node *rn; + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefix = nbr_nbma->addr; + p.prefixlen = IPV4_MAX_BITLEN; + + rn = route_node_lookup(ospf->nbr_nbma, (struct prefix *)&p); + if (rn) { + ospf_nbr_nbma_free(rn->info); + rn->info = NULL; + route_unlock_node(rn); + route_unlock_node(rn); + } +} + +static void ospf_nbr_nbma_down(struct ospf_nbr_nbma *nbr_nbma) +{ + EVENT_OFF(nbr_nbma->t_poll); + + if (nbr_nbma->nbr) { + nbr_nbma->nbr->nbr_nbma = NULL; + OSPF_NSM_EVENT_EXECUTE(nbr_nbma->nbr, NSM_KillNbr); + } + + if (nbr_nbma->oi) + listnode_delete(nbr_nbma->oi->nbr_nbma, nbr_nbma); +} + +static void ospf_nbr_nbma_add(struct ospf_nbr_nbma *nbr_nbma, + struct ospf_interface *oi) +{ + struct ospf_neighbor *nbr; + struct route_node *rn; + struct prefix p; + + if (oi->type != OSPF_IFTYPE_NBMA) + return; + + if (nbr_nbma->nbr != NULL) + return; + + if (IPV4_ADDR_SAME(&oi->nbr_self->address.u.prefix4, &nbr_nbma->addr)) + return; + + nbr_nbma->oi = oi; + listnode_add(oi->nbr_nbma, nbr_nbma); + + /* Get neighbor information from table. */ + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = nbr_nbma->addr; + + rn = route_node_get(oi->nbrs, &p); + if (rn->info) { + nbr = rn->info; + nbr->nbr_nbma = nbr_nbma; + nbr_nbma->nbr = nbr; + + route_unlock_node(rn); + } else { + nbr = rn->info = ospf_nbr_new(oi); + nbr->state = NSM_Down; + nbr->src = nbr_nbma->addr; + nbr->nbr_nbma = nbr_nbma; + nbr->priority = nbr_nbma->priority; + nbr->address = p; + + nbr_nbma->nbr = nbr; + + /* Configure BFD if interface has it. */ + ospf_neighbor_bfd_apply(nbr); + + OSPF_NSM_EVENT_EXECUTE(nbr, NSM_Start); + } +} + +void ospf_nbr_nbma_if_update(struct ospf *ospf, struct ospf_interface *oi) +{ + struct ospf_nbr_nbma *nbr_nbma; + struct route_node *rn; + struct prefix_ipv4 p; + + if (oi->type != OSPF_IFTYPE_NBMA) + return; + + for (rn = route_top(ospf->nbr_nbma); rn; rn = route_next(rn)) + if ((nbr_nbma = rn->info)) + if (nbr_nbma->oi == NULL && nbr_nbma->nbr == NULL) { + p.family = AF_INET; + p.prefix = nbr_nbma->addr; + p.prefixlen = IPV4_MAX_BITLEN; + + if (prefix_match(oi->address, + (struct prefix *)&p)) + ospf_nbr_nbma_add(nbr_nbma, oi); + } +} + +struct ospf_nbr_nbma *ospf_nbr_nbma_lookup(struct ospf *ospf, + struct in_addr nbr_addr) +{ + struct route_node *rn; + struct prefix_ipv4 p; + + p.family = AF_INET; + p.prefix = nbr_addr; + p.prefixlen = IPV4_MAX_BITLEN; + + rn = route_node_lookup(ospf->nbr_nbma, (struct prefix *)&p); + if (rn) { + route_unlock_node(rn); + return rn->info; + } + return NULL; +} + +int ospf_nbr_nbma_set(struct ospf *ospf, struct in_addr nbr_addr) +{ + struct ospf_nbr_nbma *nbr_nbma; + struct ospf_interface *oi; + struct prefix_ipv4 p; + struct route_node *rn; + struct listnode *node; + + nbr_nbma = ospf_nbr_nbma_lookup(ospf, nbr_addr); + if (nbr_nbma) + return 0; + + nbr_nbma = ospf_nbr_nbma_new(); + nbr_nbma->addr = nbr_addr; + + p.family = AF_INET; + p.prefix = nbr_addr; + p.prefixlen = IPV4_MAX_BITLEN; + + rn = route_node_get(ospf->nbr_nbma, (struct prefix *)&p); + if (rn->info) + route_unlock_node(rn); + rn->info = nbr_nbma; + + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) { + if (oi->type == OSPF_IFTYPE_NBMA) + if (prefix_match(oi->address, (struct prefix *)&p)) { + ospf_nbr_nbma_add(nbr_nbma, oi); + break; + } + } + + return 1; +} + +int ospf_nbr_nbma_unset(struct ospf *ospf, struct in_addr nbr_addr) +{ + struct ospf_nbr_nbma *nbr_nbma; + + nbr_nbma = ospf_nbr_nbma_lookup(ospf, nbr_addr); + if (nbr_nbma == NULL) + return 0; + + ospf_nbr_nbma_down(nbr_nbma); + ospf_nbr_nbma_delete(ospf, nbr_nbma); + + return 1; +} + +int ospf_nbr_nbma_priority_set(struct ospf *ospf, struct in_addr nbr_addr, + uint8_t priority) +{ + struct ospf_nbr_nbma *nbr_nbma; + + nbr_nbma = ospf_nbr_nbma_lookup(ospf, nbr_addr); + if (nbr_nbma == NULL) + return 0; + + if (nbr_nbma->priority != priority) + nbr_nbma->priority = priority; + + return 1; +} + +int ospf_nbr_nbma_priority_unset(struct ospf *ospf, struct in_addr nbr_addr) +{ + struct ospf_nbr_nbma *nbr_nbma; + + nbr_nbma = ospf_nbr_nbma_lookup(ospf, nbr_addr); + if (nbr_nbma == NULL) + return 0; + + if (nbr_nbma != OSPF_NEIGHBOR_PRIORITY_DEFAULT) + nbr_nbma->priority = OSPF_NEIGHBOR_PRIORITY_DEFAULT; + + return 1; +} + +int ospf_nbr_nbma_poll_interval_set(struct ospf *ospf, struct in_addr nbr_addr, + unsigned int interval) +{ + struct ospf_nbr_nbma *nbr_nbma; + + nbr_nbma = ospf_nbr_nbma_lookup(ospf, nbr_addr); + if (nbr_nbma == NULL) + return 0; + + if (nbr_nbma->v_poll != interval) { + nbr_nbma->v_poll = interval; + if (nbr_nbma->oi && ospf_if_is_up(nbr_nbma->oi)) { + EVENT_OFF(nbr_nbma->t_poll); + OSPF_POLL_TIMER_ON(nbr_nbma->t_poll, ospf_poll_timer, + nbr_nbma->v_poll); + } + } + + return 1; +} + +int ospf_nbr_nbma_poll_interval_unset(struct ospf *ospf, struct in_addr addr) +{ + struct ospf_nbr_nbma *nbr_nbma; + + nbr_nbma = ospf_nbr_nbma_lookup(ospf, addr); + if (nbr_nbma == NULL) + return 0; + + if (nbr_nbma->v_poll != OSPF_POLL_INTERVAL_DEFAULT) + nbr_nbma->v_poll = OSPF_POLL_INTERVAL_DEFAULT; + + return 1; +} + +/* + * Update socket bufsize(s), usually after config change + */ +void ospf_update_bufsize(struct ospf *ospf, uint32_t recvsize, + uint32_t sendsize) +{ + enum ospf_sock_type_e type = OSPF_SOCK_NONE; + + /* Figure out whether there's been a change */ + if (recvsize != ospf->recv_sock_bufsize) { + type = OSPF_SOCK_RECV; + ospf->recv_sock_bufsize = recvsize; + + if (sendsize != ospf->send_sock_bufsize) { + type = OSPF_SOCK_BOTH; + ospf->send_sock_bufsize = sendsize; + } + } else if (sendsize != ospf->send_sock_bufsize) { + type = OSPF_SOCK_SEND; + ospf->send_sock_bufsize = sendsize; + } + + if (type != OSPF_SOCK_NONE) + ospf_sock_bufsize_update(ospf, ospf->fd, type); +} + +void ospf_master_init(struct event_loop *master) +{ + memset(&ospf_master, 0, sizeof(ospf_master)); + + om = &ospf_master; + om->ospf = list_new(); + om->master = master; +} + +/* Link OSPF instance to VRF. */ +void ospf_vrf_link(struct ospf *ospf, struct vrf *vrf) +{ + ospf->vrf_id = vrf->vrf_id; + if (vrf->info != (void *)ospf) + vrf->info = (void *)ospf; +} + +/* Unlink OSPF instance from VRF. */ +void ospf_vrf_unlink(struct ospf *ospf, struct vrf *vrf) +{ + if (vrf->info == (void *)ospf) + vrf->info = NULL; + ospf->vrf_id = VRF_UNKNOWN; +} + +/* This is hook function for vrf create called as part of vrf_init */ +static int ospf_vrf_new(struct vrf *vrf) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: VRF Created: %s(%u)", __func__, vrf->name, + vrf->vrf_id); + + return 0; +} + +/* This is hook function for vrf delete call as part of vrf_init */ +static int ospf_vrf_delete(struct vrf *vrf) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: VRF Deletion: %s(%u)", __func__, vrf->name, + vrf->vrf_id); + + return 0; +} + +static void ospf_set_redist_vrf_bitmaps(struct ospf *ospf, bool set) +{ + int type; + struct list *red_list; + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + red_list = ospf->redist[type]; + if (!red_list) + continue; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: setting redist vrf %d bitmap for type %d", + __func__, ospf->vrf_id, type); + if (set) + vrf_bitmap_set(&zclient->redist[AFI_IP][type], + ospf->vrf_id); + else + vrf_bitmap_unset(&zclient->redist[AFI_IP][type], + ospf->vrf_id); + } + + red_list = ospf->redist[DEFAULT_ROUTE]; + if (red_list) { + if (set) + vrf_bitmap_set(&zclient->default_information[AFI_IP], + ospf->vrf_id); + else + vrf_bitmap_unset(&zclient->default_information[AFI_IP], + ospf->vrf_id); + } +} + +/* Enable OSPF VRF instance */ +static int ospf_vrf_enable(struct vrf *vrf) +{ + struct ospf *ospf = NULL; + vrf_id_t old_vrf_id; + int ret = 0; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: VRF %s id %u enabled", __func__, vrf->name, + vrf->vrf_id); + + ospf = ospf_lookup_by_name(vrf->name); + if (ospf) { + old_vrf_id = ospf->vrf_id; + /* We have instance configured, link to VRF and make it "up". */ + ospf_vrf_link(ospf, vrf); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: ospf linked to vrf %s vrf_id %u (old id %u)", + __func__, vrf->name, ospf->vrf_id, old_vrf_id); + + if (old_vrf_id != ospf->vrf_id) { + ospf_set_redist_vrf_bitmaps(ospf, true); + + /* start zebra redist to us for new vrf */ + ospf_zebra_vrf_register(ospf); + + ret = ospf_sock_init(ospf); + if (ret < 0 || ospf->fd <= 0) + return 0; + event_add_read(master, ospf_read, ospf, ospf->fd, + &ospf->t_read); + ospf->oi_running = 1; + ospf_router_id_update(ospf); + } + } + + return 0; +} + +/* Disable OSPF VRF instance */ +static int ospf_vrf_disable(struct vrf *vrf) +{ + struct ospf *ospf = NULL; + vrf_id_t old_vrf_id = VRF_UNKNOWN; + + if (vrf->vrf_id == VRF_DEFAULT) + return 0; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: VRF %s id %d disabled.", __func__, vrf->name, + vrf->vrf_id); + + ospf = ospf_lookup_by_name(vrf->name); + if (ospf) { + old_vrf_id = ospf->vrf_id; + + ospf_zebra_vrf_deregister(ospf); + + ospf_set_redist_vrf_bitmaps(ospf, false); + + /* We have instance configured, unlink + * from VRF and make it "down". + */ + ospf_vrf_unlink(ospf, vrf); + ospf->oi_running = 0; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: ospf old_vrf_id %d unlinked", __func__, + old_vrf_id); + EVENT_OFF(ospf->t_read); + close(ospf->fd); + ospf->fd = -1; + } + + /* Note: This is a callback, the VRF will be deleted by the caller. */ + return 0; +} + +void ospf_vrf_init(void) +{ + vrf_init(ospf_vrf_new, ospf_vrf_enable, ospf_vrf_disable, + ospf_vrf_delete); +} + +void ospf_vrf_terminate(void) +{ + vrf_terminate(); +} + +const char *ospf_vrf_id_to_name(vrf_id_t vrf_id) +{ + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + + return vrf ? vrf->name : "NIL"; +} + +const char *ospf_get_name(const struct ospf *ospf) +{ + if (ospf->name) + return ospf->name; + else + return VRF_DEFAULT_NAME; +} diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h new file mode 100644 index 0000000..2ab7db1 --- /dev/null +++ b/ospfd/ospfd.h @@ -0,0 +1,820 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPFd main header. + * Copyright (C) 1998, 99, 2000 Kunihiro Ishiguro, Toshiaki Takada + */ + +#ifndef _ZEBRA_OSPFD_H +#define _ZEBRA_OSPFD_H + +#include +#include "typesafe.h" +#include "qobj.h" +#include "libospf.h" +#include "ldp_sync.h" + +#include "filter.h" +#include "log.h" +#include "vrf.h" + +#include "ospf_memory.h" +#include "ospf_dump_api.h" + +#define OSPF_VERSION 2 + +/* VTY port number. */ +#define OSPF_VTY_PORT 2604 + +/* IP TTL for OSPF protocol. */ +#define OSPF_IP_TTL 1 +#define OSPF_VL_IP_TTL 100 + +/* Default configuration file name for ospfd. */ +#define OSPF_DEFAULT_CONFIG "ospfd.conf" + +#define OSPF_NSSA_TRANS_STABLE_DEFAULT 40 + +#define OSPF_ALLSPFROUTERS 0xe0000005 /* 224.0.0.5 */ +#define OSPF_ALLDROUTERS 0xe0000006 /* 224.0.0.6 */ + +/* OSPF Authentication Type. */ +#define OSPF_AUTH_NULL 0 +#define OSPF_AUTH_SIMPLE 1 +#define OSPF_AUTH_CRYPTOGRAPHIC 2 +/* For Interface authentication setting default */ +#define OSPF_AUTH_NOTSET -1 +/* For the consumption and sanity of the command handler */ +/* DO NIOT REMOVE!!! Need to detect whether a value has + been given or not in VLink command handlers */ +#define OSPF_AUTH_CMD_NOTSEEN -2 + +/* OSPF options. */ +#define OSPF_OPTION_MT 0x01 /* M/T */ +#define OSPF_OPTION_E 0x02 +#define OSPF_OPTION_MC 0x04 +#define OSPF_OPTION_NP 0x08 +#define OSPF_OPTION_EA 0x10 +#define OSPF_OPTION_DC 0x20 +#define OSPF_OPTION_O 0x40 +#define OSPF_OPTION_DN 0x80 + +/* OSPF Database Description flags. */ +#define OSPF_DD_FLAG_MS 0x01 +#define OSPF_DD_FLAG_M 0x02 +#define OSPF_DD_FLAG_I 0x04 +#define OSPF_DD_FLAG_ALL 0x07 + +#define OSPF_LS_REFRESH_SHIFT (60 * 15) +#define OSPF_LS_REFRESH_JITTER 60 + +/* Default socket buffer size */ +#define OSPF_DEFAULT_SOCK_BUFSIZE (8 * 1024 * 1024) + +/* OSPF config processing timer thread */ +extern struct event *t_ospf_cfg; + +struct ospf_external { + unsigned short instance; + struct route_table *external_info; +}; + +/* OSPF master for system wide configuration and variables. */ +struct ospf_master { + /* OSPF instance. */ + struct list *ospf; + + /* OSPF thread master. */ + struct event_loop *master; + + /* Various OSPF global configuration. */ + uint8_t options; +#define OSPF_MASTER_SHUTDOWN (1 << 0) /* deferred-shutdown */ +}; + +struct ospf_redist { + unsigned short instance; + + /* Redistribute metric info. */ + struct { + int type; /* External metric type (E1 or E2). */ + int value; /* Value for static metric (24-bit). + -1 means metric value is not set. */ + } dmetric; + + /* For redistribute route map. */ + struct { + char *name; + struct route_map *map; + } route_map; /* +1 is for default-information */ +#define ROUTEMAP_NAME(R) (R->route_map.name) +#define ROUTEMAP(R) (R->route_map.map) +}; + +/* OSPF area flood reduction info */ +struct ospf_area_fr_info { + bool enabled; /* Area support for Flood Reduction */ + bool configured; /* Flood Reduction configured per area knob */ + bool state_changed; /* flood reduction state change info */ + int router_lsas_recv_dc_bit; /* Number of unique router lsas + * received with DC bit set. + * (excluding self) + */ + bool area_ind_lsa_recvd; /* Indication lsa received in this area */ + bool area_dc_clear; /* Area has atleast one lsa with dc bit 0( + * excluding indication lsa) + */ + struct ospf_lsa *indication_lsa_self; /* Indication LSA generated + * in the area. + */ +}; + +/* ospf->config */ +enum { + OSPF_RFC1583_COMPATIBLE = (1 << 0), + OSPF_OPAQUE_CAPABLE = (1 << 2), + OSPF_LOG_ADJACENCY_CHANGES = (1 << 3), + OSPF_LOG_ADJACENCY_DETAIL = (1 << 4), + OSPF_SEND_EXTRA_DATA_TO_ZEBRA = (1 << 5), +}; + +/* TI-LFA */ +enum protection_type { + OSPF_TI_LFA_UNDEFINED_PROTECTION, + OSPF_TI_LFA_LINK_PROTECTION, + OSPF_TI_LFA_NODE_PROTECTION, +}; + +/* OSPF nonstop forwarding aka Graceful Restart */ +struct ospf_gr_info { + bool restart_support; + bool restart_in_progress; + bool prepare_in_progress; + bool finishing_restart; + uint32_t grace_period; + int reason; + char *exit_reason; + struct event *t_grace_period; +}; + +/* OSPF instance structure. */ +struct ospf { + /* OSPF's running state based on the '[no] router ospf []' + * config. */ + uint8_t oi_running; + + /* OSPF instance ID */ + unsigned short instance; + + /* OSPF Router ID. */ + struct in_addr router_id; /* Configured automatically. */ + struct in_addr router_id_static; /* Configured manually. */ + struct in_addr router_id_zebra; + + vrf_id_t vrf_id; /* VRF Id */ + char *name; /* VRF name */ + + /* ABR/ASBR internal flags. */ + uint8_t flags; +#define OSPF_FLAG_ABR 0x0001 +#define OSPF_FLAG_ASBR 0x0002 + + /* ABR type. */ + uint8_t abr_type; +#define OSPF_ABR_UNKNOWN 0 +#define OSPF_ABR_STAND 1 +#define OSPF_ABR_IBM 2 +#define OSPF_ABR_CISCO 3 +#define OSPF_ABR_SHORTCUT 4 +#define OSPF_ABR_DEFAULT OSPF_ABR_CISCO + + /* NSSA ABR */ + uint8_t anyNSSA; /* Bump for every NSSA attached. */ + + /* Configuration bitmask, refer to enum above */ + uint8_t config; + + /* Opaque-LSA administrative flags. */ + uint8_t opaque; +#define OPAQUE_OPERATION_READY_BIT (1 << 0) + + /* RFC3137 stub router. Configured time to stay stub / max-metric */ + unsigned int stub_router_startup_time; /* seconds */ + unsigned int stub_router_shutdown_time; /* seconds */ +#define OSPF_STUB_ROUTER_UNCONFIGURED 0 + uint8_t stub_router_admin_set; +#define OSPF_STUB_ROUTER_ADMINISTRATIVE_SET 1 +#define OSPF_STUB_ROUTER_ADMINISTRATIVE_UNSET 0 + +#define OSPF_STUB_MAX_METRIC_SUMMARY_COST 0x00ff0000 + + /* LSA timers */ + unsigned int min_ls_interval; /* minimum delay between LSAs (in msec) */ + unsigned int min_ls_arrival; /* minimum interarrival time between LSAs + (in msec) */ + + /* SPF parameters */ + unsigned int spf_delay; /* SPF delay time. */ + unsigned int spf_holdtime; /* SPF hold time. */ + unsigned int spf_max_holdtime; /* SPF maximum-holdtime */ + unsigned int + spf_hold_multiplier; /* Adaptive multiplier for hold time */ + + int default_originate; /* Default information originate. */ +#define DEFAULT_ORIGINATE_NONE 0 +#define DEFAULT_ORIGINATE_ZEBRA 1 +#define DEFAULT_ORIGINATE_ALWAYS 2 + uint32_t ref_bandwidth; /* Reference Bandwidth (Kbps). */ + struct route_table *networks; /* OSPF config networks. */ + struct list *vlinks; /* Configured Virtual-Links. */ + struct list *areas; /* OSPF areas. */ + struct route_table *nbr_nbma; + struct ospf_area *backbone; /* Pointer to the Backbone Area. */ + + struct list *oiflist; /* ospf interfaces */ + uint8_t passive_interface_default; /* passive-interface default */ + + /* LSDB of AS-external-LSAs. */ + struct ospf_lsdb *lsdb; + + /* Flags. */ + int ase_calc; /* ASE calculation flag. */ + + struct list *opaque_lsa_self; /* Type-11 Opaque-LSAs */ + + /* Routing tables. */ + struct route_table *old_table; /* Old routing table. */ + struct route_table *new_table; /* Current routing table. */ + + struct route_table *oall_rtrs; /* Old router RT. */ + struct route_table *all_rtrs; /* New routers RT. */ + + struct route_table *old_rtrs; /* Old ABR/ASBR RT. */ + struct route_table *new_rtrs; /* New ABR/ASBR RT. */ + + struct route_table *new_external_route; /* New External Route. */ + struct route_table *old_external_route; /* Old External Route. */ + + struct route_table *external_lsas; /* Database of external LSAs, + prefix is LSA's adv. network*/ + + /* Time stamps */ + struct timeval ts_spf; /* SPF calculation time stamp. */ + struct timeval ts_spf_duration; /* Execution time of last SPF */ + + struct route_table *maxage_lsa; /* List of MaxAge LSA for deletion. */ + int redistribute; /* Num of redistributed protocols. */ + + /* Threads. */ + struct event *t_abr_task; /* ABR task timer. */ + struct event *t_abr_fr; /* ABR FR timer. */ + struct event *t_asbr_check; /* ASBR check timer. */ + struct event *t_asbr_redist_update; /* ASBR redistribution update + timer. */ + struct event *t_distribute_update; /* Distirbute list update timer. */ + struct event *t_spf_calc; /* SPF calculation timer. */ + struct event *t_ase_calc; /* ASE calculation timer. */ + struct event *t_opaque_lsa_self; /* Type-11 Opaque-LSAs origin event. */ + struct event *t_sr_update; /* Segment Routing update timer */ + + unsigned int maxage_delay; /* Delay on Maxage remover timer, sec */ + struct event *t_maxage; /* MaxAge LSA remover timer. */ + struct event *t_maxage_walker; /* MaxAge LSA checking timer. */ + + struct event + *t_deferred_shutdown; /* deferred/stub-router shutdown timer*/ + + struct event *t_write; +#define OSPF_WRITE_INTERFACE_COUNT_DEFAULT 20 + struct event *t_default_routemap_timer; + + int write_oi_count; /* Num of packets sent per thread invocation */ + struct event *t_read; + int fd; + struct stream *ibuf; + struct list *oi_write_q; + + /* Distribute lists out of other route sources. */ + struct { + char *name; + struct access_list *list; + } dlist[ZEBRA_ROUTE_MAX]; +#define DISTRIBUTE_NAME(O,T) (O)->dlist[T].name +#define DISTRIBUTE_LIST(O,T) (O)->dlist[T].list + + /* OSPF redistribute configuration */ + struct list *redist[ZEBRA_ROUTE_MAX + 1]; + + /* Redistribute tag info. */ + route_tag_t + dtag[ZEBRA_ROUTE_MAX + 1]; // Pending: cant configure as of now + + int default_metric; /* Default metric for redistribute. */ + + /* NSSA default-information-originate */ + struct { + /* # of NSSA areas requesting default information */ + uint16_t refcnt; + + /* + * Whether a default route known through non-OSPF protocol is + * present in the RIB. + */ + bool status; + } nssa_default_import_check; + +#define OSPF_LSA_REFRESHER_GRANULARITY 10 +#define OSPF_LSA_REFRESHER_SLOTS \ + ((OSPF_LS_REFRESH_TIME + OSPF_LS_REFRESH_SHIFT) \ + / OSPF_LSA_REFRESHER_GRANULARITY \ + + 1) + struct { + uint16_t index; + struct list *qs[OSPF_LSA_REFRESHER_SLOTS]; + } lsa_refresh_queue; + + struct event *t_lsa_refresher; + time_t lsa_refresher_started; +#define OSPF_LSA_REFRESH_INTERVAL_DEFAULT 10 + uint16_t lsa_refresh_interval; + uint16_t lsa_refresh_timer; + + /* Distance parameter. */ + uint8_t distance_all; + uint8_t distance_intra; + uint8_t distance_inter; + uint8_t distance_external; + + /* Statistics for LSA origination. */ + uint32_t lsa_originate_count; + + /* Statistics for LSA used for new instantiation. */ + uint32_t rx_lsa_count; + + struct route_table *distance_table; + + /* Used during ospf instance going down send LSDB + * update to neighbors immediatly */ + uint8_t inst_shutdown; + + /* Enable or disable sending proactive ARP requests. */ + bool proactive_arp; +#define OSPF_PROACTIVE_ARP_DEFAULT true + + /* Redistributed external information. */ + struct list *external[ZEBRA_ROUTE_MAX + 1]; +#define EXTERNAL_INFO(E) (E->external_info) + + /* Graceful restart Helper supported configs*/ + /* Supported grace interval*/ + uint32_t supported_grace_time; + + /* Helper support + * Supported : True + * Not Supported : False. + */ + bool is_helper_supported; + + /* Support for strict LSA check. + * if it is set,Helper aborted + * upon a TOPO change. + */ + bool strict_lsa_check; + + /* Support as HELPER only for + * planned restarts. + */ + bool only_planned_restart; + + /* This list contains the advertisement + * routerids which are not support for HELPERs. + */ + struct hash *enable_rtr_list; + + /* HELPER for number of active + * RESTARTERs. + */ + uint16_t active_restarter_cnt; + + /* last HELPER exit reason */ + uint32_t last_exit_reason; + + /* delay timer to process external routes + * with summary address. + */ + struct event *t_external_aggr; + + /* delay interval in seconds */ + uint16_t aggr_delay_interval; + + /* Table of configured Aggregate addresses */ + struct route_table *rt_aggr_tbl; + + /* used as argument for aggr delay + * timer thread. + */ + int aggr_action; + + /* Max number of multiple paths + * to support ECMP. + */ + uint16_t max_multipath; + + /* MPLS LDP-IGP Sync */ + struct ldp_sync_info_cmd ldp_sync_cmd; + + /* OSPF Graceful Restart info */ + struct ospf_gr_info gr_info; + + /* TI-LFA support for all interfaces. */ + bool ti_lfa_enabled; + enum protection_type ti_lfa_protection_type; + + /* Flood Reduction configuration state */ + bool fr_configured; + + /* Socket buffer sizes */ + uint32_t recv_sock_bufsize; + uint32_t send_sock_bufsize; + + /* Per-interface write socket */ + bool intf_socket_enabled; + + QOBJ_FIELDS; +}; +DECLARE_QOBJ_TYPE(ospf); + +enum ospf_ti_lfa_p_q_space_adjacency { + OSPF_TI_LFA_P_Q_SPACE_ADJACENT, + OSPF_TI_LFA_P_Q_SPACE_NON_ADJACENT, +}; + +enum ospf_ti_lfa_node_type { + OSPF_TI_LFA_UNDEFINED_NODE, + OSPF_TI_LFA_PQ_NODE, + OSPF_TI_LFA_P_NODE, + OSPF_TI_LFA_Q_NODE, +}; + +struct ospf_ti_lfa_node_info { + struct vertex *node; + enum ospf_ti_lfa_node_type type; + struct in_addr nexthop; +}; + +struct ospf_ti_lfa_inner_backup_path_info { + struct ospf_ti_lfa_node_info p_node_info; + struct ospf_ti_lfa_node_info q_node_info; + struct mpls_label_stack *label_stack; +}; + +struct protected_resource { + enum protection_type type; + + /* Link Protection */ + struct router_lsa_link *link; + + /* Node Protection */ + struct in_addr router_id; +}; + +PREDECL_RBTREE_UNIQ(q_spaces); +struct q_space { + struct vertex *root; + struct list *vertex_list; + struct mpls_label_stack *label_stack; + struct in_addr nexthop; + struct list *pc_path; + struct ospf_ti_lfa_node_info *p_node_info; + struct ospf_ti_lfa_node_info *q_node_info; + struct q_spaces_item q_spaces_item; +}; + +PREDECL_RBTREE_UNIQ(p_spaces); +struct p_space { + struct vertex *root; + struct protected_resource *protected_resource; + struct q_spaces_head *q_spaces; + struct list *vertex_list; + struct vertex *pc_spf; + struct list *pc_vertex_list; + struct p_spaces_item p_spaces_item; +}; + +/* OSPF area structure. */ +struct ospf_area { + /* OSPF instance. */ + struct ospf *ospf; + + /* Zebra interface list belonging to the area. */ + struct list *oiflist; + + /* Area ID. */ + struct in_addr area_id; + + /* Area ID format. */ + int area_id_fmt; +#define OSPF_AREA_ID_FMT_DOTTEDQUAD 1 +#define OSPF_AREA_ID_FMT_DECIMAL 2 + + /* Address range. */ + struct list *address_range; + + /* Configured variables. */ + int external_routing; /* ExternalRoutingCapability. */ + int no_summary; /* Don't inject summaries into stub.*/ + int shortcut_configured; /* Area configured as shortcut. */ +#define OSPF_SHORTCUT_DEFAULT 0 +#define OSPF_SHORTCUT_ENABLE 1 +#define OSPF_SHORTCUT_DISABLE 2 + int shortcut_capability; /* Other ABRs agree on S-bit */ + uint32_t default_cost; /* StubDefaultCost. */ + int auth_type; /* Authentication type. */ + int suppress_fa; /* Suppress forwarding address in NSSA ABR */ + + uint8_t NSSATranslatorRole; /* NSSA configured role */ +#define OSPF_NSSA_ROLE_NEVER 0 +#define OSPF_NSSA_ROLE_CANDIDATE 1 +#define OSPF_NSSA_ROLE_ALWAYS 2 + uint8_t NSSATranslatorState; /* NSSA operational role */ +#define OSPF_NSSA_TRANSLATE_DISABLED 0 +#define OSPF_NSSA_TRANSLATE_ENABLED 1 + int NSSATranslatorStabilityInterval; + + uint8_t transit; /* TransitCapability. */ +#define OSPF_TRANSIT_FALSE 0 +#define OSPF_TRANSIT_TRUE 1 + struct route_table *ranges; /* Configured Area Ranges. */ + struct route_table *nssa_ranges; /* Configured NSSA Area Ranges. */ + + /* RFC3137 stub router state flags for area */ + uint8_t stub_router_state; +#define OSPF_AREA_ADMIN_STUB_ROUTED (1 << 0) /* admin stub-router set */ +#define OSPF_AREA_IS_STUB_ROUTED (1 << 1) /* stub-router active */ +#define OSPF_AREA_WAS_START_STUB_ROUTED (1 << 2) /* startup SR was done */ + /* Area related LSDBs[Type1-4]. */ + struct ospf_lsdb *lsdb; + + /* Self-originated LSAs. */ + struct ospf_lsa *router_lsa_self; + struct list *opaque_lsa_self; /* Type-10 Opaque-LSAs */ + + /* Area announce list. */ + struct { + char *name; + struct access_list *list; + } _export; +#define EXPORT_NAME(A) (A)->_export.name +#define EXPORT_LIST(A) (A)->_export.list + + /* Area acceptance list. */ + struct { + char *name; + struct access_list *list; + } import; +#define IMPORT_NAME(A) (A)->import.name +#define IMPORT_LIST(A) (A)->import.list + + /* Type 3 LSA Area prefix-list. */ + struct { + char *name; + struct prefix_list *list; + } plist_in; +#define PREFIX_LIST_IN(A) (A)->plist_in.list +#define PREFIX_NAME_IN(A) (A)->plist_in.name + + struct { + char *name; + struct prefix_list *list; + } plist_out; +#define PREFIX_LIST_OUT(A) (A)->plist_out.list +#define PREFIX_NAME_OUT(A) (A)->plist_out.name + + /* NSSA default-information-originate */ + struct { + bool enabled; + int metric_type; + int metric_value; + } nssa_default_originate; + + /* Shortest Path Tree. */ + struct vertex *spf; + struct list *spf_vertex_list; + + bool spf_dry_run; /* flag for checking if the SPF calculation is + intended for the local RIB */ + bool spf_root_node; /* flag for checking if the calculating node is the + root node of the SPF tree */ + + /* TI-LFA protected link for SPF calculations */ + struct protected_resource *spf_protected_resource; + + /* P/Q spaces for TI-LFA */ + struct p_spaces_head *p_spaces; + + /* Threads. */ + struct event *t_stub_router; /* Stub-router timer */ + struct event *t_opaque_lsa_self; /* Type-10 Opaque-LSAs origin. */ + + /* Statistics field. */ + uint32_t spf_calculation; /* SPF Calculation Count. */ + + /* reverse SPF (used for TI-LFA Q spaces) */ + bool spf_reversed; + + /* Time stamps. */ + struct timeval ts_spf; /* SPF calculation time stamp. */ + + /* Router count. */ + uint32_t abr_count; /* ABR router in this area. */ + uint32_t asbr_count; /* ASBR router in this area. */ + + /* Counters. */ + uint32_t act_ints; /* Active interfaces. */ + uint32_t full_nbrs; /* Fully adjacent neighbors. */ + uint32_t full_vls; /* Fully adjacent virtual neighbors. */ + + struct ospf_area_fr_info fr_info; /* Flood reduction info. */ +}; + +/* OSPF config network structure. */ +struct ospf_network { + /* Area ID. */ + struct in_addr area_id; + int area_id_fmt; +}; + +/* OSPF NBMA neighbor structure. */ +struct ospf_nbr_nbma { + /* Neighbor IP address. */ + struct in_addr addr; + + /* OSPF interface. */ + struct ospf_interface *oi; + + /* OSPF neighbor structure. */ + struct ospf_neighbor *nbr; + + /* Neighbor priority. */ + uint8_t priority; + + /* Poll timer value. */ + uint32_t v_poll; + + /* Poll timer thread. */ + struct event *t_poll; + + /* State change. */ + uint32_t state_change; +}; + +/* Macro. */ +#define OSPF_AREA_SAME(X, Y) \ + (memcmp((X->area_id), (Y->area_id), IPV4_MAX_BYTELEN) == 0) + +#define IS_OSPF_ABR(O) ((O)->flags & OSPF_FLAG_ABR) +#define IS_OSPF_ASBR(O) ((O)->flags & OSPF_FLAG_ASBR) + +#define OSPF_IS_AREA_ID_BACKBONE(I) ((I).s_addr == OSPF_AREA_BACKBONE) +#define OSPF_IS_AREA_BACKBONE(A) OSPF_IS_AREA_ID_BACKBONE ((A)->area_id) + +#ifdef roundup +# define ROUNDUP(val, gran) roundup(val, gran) +#else /* roundup */ +# define ROUNDUP(val, gran) (((val) - 1 | (gran) - 1) + 1) +#endif /* roundup */ + +#define LSA_OPTIONS_GET(area) \ + (((area)->external_routing == OSPF_AREA_DEFAULT) ? OSPF_OPTION_E : 0) +#define LSA_OPTIONS_NSSA_GET(area) \ + (((area)->external_routing == OSPF_AREA_NSSA) ? OSPF_OPTION_NP : 0) + +#define OSPF_TIMER_ON(T, F, V) event_add_timer(master, (F), ospf, (V), &(T)) +#define OSPF_AREA_TIMER_ON(T, F, V) \ + event_add_timer(master, (F), area, (V), &(T)) +#define OSPF_POLL_TIMER_ON(T, F, V) \ + event_add_timer(master, (F), nbr_nbma, (V), &(T)) + +/* Extern variables. */ +extern struct ospf_master *om; +extern unsigned short ospf_instance; +extern const int ospf_redistributed_proto_max; +extern struct zclient *zclient; +extern struct event_loop *master; +extern int ospf_zlog; +extern struct zebra_privs_t ospfd_privs; + +/* Prototypes. */ +extern const char *ospf_redist_string(unsigned int route_type); +extern struct ospf *ospf_lookup_instance(unsigned short instance); +extern struct ospf *ospf_lookup(unsigned short instance, const char *name); +extern struct ospf *ospf_get(unsigned short instance, const char *name, + bool *created); +extern struct ospf *ospf_new_alloc(unsigned short instance, const char *name); +extern struct ospf *ospf_lookup_by_inst_name(unsigned short instance, + const char *name); +extern struct ospf *ospf_lookup_by_vrf_id(vrf_id_t vrf_id); +extern uint32_t ospf_count_area_params(struct ospf *ospf); +extern void ospf_finish(struct ospf *ospf); +extern void ospf_process_refresh_data(struct ospf *ospf, bool reset); +extern void ospf_router_id_update(struct ospf *ospf); +extern void ospf_process_reset(struct ospf *ospf); +extern void ospf_neighbor_reset(struct ospf *ospf, struct in_addr nbr_id, + const char *nbr_str); +extern int ospf_network_set(struct ospf *ospf, struct prefix_ipv4 *p, + struct in_addr area_id, int df); +extern int ospf_network_unset(struct ospf *ospf, struct prefix_ipv4 *p, + struct in_addr aread_id); +extern int ospf_area_display_format_set(struct ospf *ospf, + struct ospf_area *area, int df); +extern int ospf_area_stub_set(struct ospf *ospf, struct in_addr area_id); +extern int ospf_area_stub_unset(struct ospf *ospf, struct in_addr area_id); +extern int ospf_area_no_summary_set(struct ospf *ospf, struct in_addr area_id); +extern int ospf_area_no_summary_unset(struct ospf *ospf, + struct in_addr area_id); +extern int ospf_area_nssa_set(struct ospf *ospf, struct in_addr area_id); +extern int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id); +extern int ospf_area_nssa_suppress_fa_set(struct ospf *ospf, + struct in_addr area_id); +extern int ospf_area_nssa_suppress_fa_unset(struct ospf *ospf, + struct in_addr area_id); +extern int ospf_area_nssa_translator_role_set(struct ospf *ospf, + struct in_addr area_id, int role); +extern void ospf_area_nssa_default_originate_set(struct ospf *ospf, + struct in_addr area_id, + int metric, int metric_type); +extern void ospf_area_nssa_default_originate_unset(struct ospf *ospf, + struct in_addr area_id); +extern int ospf_area_export_list_set(struct ospf *ospf, + struct ospf_area *area_id, + const char *list_name); +extern int ospf_area_export_list_unset(struct ospf *ospf, + struct ospf_area *area_id); +extern int ospf_area_import_list_set(struct ospf *ospf, + struct ospf_area *area_id, + const char *name); +extern int ospf_area_import_list_unset(struct ospf *ospf, + struct ospf_area *area_id); +extern int ospf_area_shortcut_set(struct ospf *ospf, struct ospf_area *area_id, + int mode); +extern int ospf_area_shortcut_unset(struct ospf *ospf, + struct ospf_area *area_id); +extern int ospf_timers_refresh_set(struct ospf *ospf, int interval); +extern int ospf_timers_refresh_unset(struct ospf *ospf); +void ospf_area_lsdb_discard_delete(struct ospf_area *area); +extern int ospf_nbr_nbma_set(struct ospf *ospf, struct in_addr nbr_addr); +extern int ospf_nbr_nbma_unset(struct ospf *ospf, struct in_addr nbr_addr); +extern int ospf_nbr_nbma_priority_set(struct ospf *ospf, + struct in_addr nbr_addr, + uint8_t priority); +extern int ospf_nbr_nbma_priority_unset(struct ospf *ospf, + struct in_addr nbr_addr); +extern int ospf_nbr_nbma_poll_interval_set(struct ospf *ospf, + struct in_addr nbr_addr, + unsigned int interval); +extern int ospf_nbr_nbma_poll_interval_unset(struct ospf *ospf, + struct in_addr addr); +extern void ospf_if_update(struct ospf *ospf, struct interface *ifp); +extern void ospf_ls_upd_queue_empty(struct ospf_interface *oi); +extern void ospf_terminate(void); +extern void ospf_nbr_nbma_if_update(struct ospf *ospf, + struct ospf_interface *oi); +extern struct ospf_nbr_nbma *ospf_nbr_nbma_lookup(struct ospf *ospf, + struct in_addr nbr_addr); +extern int ospf_oi_count(struct interface *ifp); + +extern struct ospf_area *ospf_area_new(struct ospf *ospf, + struct in_addr area_id); +extern struct ospf_area *ospf_area_get(struct ospf *ospf, + struct in_addr area_id); +extern void ospf_area_check_free(struct ospf *ospf, struct in_addr area_id); +extern struct ospf_area *ospf_area_lookup_by_area_id(struct ospf *ospf, + struct in_addr area_id); +extern void ospf_area_add_if(struct ospf_area *oa, struct ospf_interface *oi); +extern void ospf_area_del_if(struct ospf_area *oa, struct ospf_interface *oi); + +extern void ospf_interface_area_set(struct ospf *ospf, struct interface *ifp); +extern void ospf_interface_area_unset(struct ospf *ospf, struct interface *ifp); + +extern void ospf_route_map_init(void); + +extern void ospf_master_init(struct event_loop *master); +extern void ospf_vrf_init(void); +extern void ospf_vrf_terminate(void); +extern void ospf_vrf_link(struct ospf *ospf, struct vrf *vrf); +extern void ospf_vrf_unlink(struct ospf *ospf, struct vrf *vrf); +const char *ospf_vrf_id_to_name(vrf_id_t vrf_id); +int ospf_area_nssa_no_summary_set(struct ospf *ospf, struct in_addr area_id); + +const char *ospf_get_name(const struct ospf *ospf); +extern struct ospf_interface *add_ospf_interface(struct connected *co, + struct ospf_area *area); +/* Update socket bufsize(s), after config change */ +void ospf_update_bufsize(struct ospf *ospf, uint32_t recvsize, + uint32_t sendsize); + +extern int p_spaces_compare_func(const struct p_space *a, + const struct p_space *b); +extern int q_spaces_compare_func(const struct q_space *a, + const struct q_space *b); + +#endif /* _ZEBRA_OSPFD_H */ diff --git a/ospfd/subdir.am b/ospfd/subdir.am new file mode 100644 index 0000000..4803aae --- /dev/null +++ b/ospfd/subdir.am @@ -0,0 +1,127 @@ +# +# ospfd +# + +if OSPFD +noinst_LIBRARIES += ospfd/libfrrospf.a +noinst_LIBRARIES += ospfd/libfrrospfclient.a +sbin_PROGRAMS += ospfd/ospfd +vtysh_daemons += ospfd +if SNMP +module_LTLIBRARIES += ospfd/ospfd_snmp.la +endif +man8 += $(MANBUILD)/frr-ospfd.8 +endif + +ospfd_libfrrospfclient_a_SOURCES = \ + ospfd/ospf_api.c \ + ospfd/ospf_dump_api.c \ + #end + +ospfd_libfrrospf_a_SOURCES = \ + ospfd/ospf_abr.c \ + ospfd/ospf_api.c \ + ospfd/ospf_apiserver.c \ + ospfd/ospf_asbr.c \ + ospfd/ospf_ase.c \ + ospfd/ospf_bfd.c \ + ospfd/ospf_dump.c \ + ospfd/ospf_dump_api.c \ + ospfd/ospf_errors.c \ + ospfd/ospf_ext.c \ + ospfd/ospf_flood.c \ + ospfd/ospf_gr.c \ + ospfd/ospf_ia.c \ + ospfd/ospf_interface.c \ + ospfd/ospf_ism.c \ + ospfd/ospf_ldp_sync.c \ + ospfd/ospf_lsa.c \ + ospfd/ospf_lsdb.c \ + ospfd/ospf_memory.c \ + ospfd/ospf_neighbor.c \ + ospfd/ospf_network.c \ + ospfd/ospf_nsm.c \ + ospfd/ospf_opaque.c \ + ospfd/ospf_packet.c \ + ospfd/ospf_ri.c \ + ospfd/ospf_route.c \ + ospfd/ospf_routemap.c \ + ospfd/ospf_routemap_nb.c \ + ospfd/ospf_routemap_nb_config.c \ + ospfd/ospf_spf.c \ + ospfd/ospf_ti_lfa.c \ + ospfd/ospf_sr.c \ + ospfd/ospf_te.c \ + ospfd/ospf_vty.c \ + ospfd/ospf_zebra.c \ + ospfd/ospfd.c \ + ospfd/ospf_gr_helper.c \ + ospfd/ospf_auth.c \ + # end + +if OSPFD +ospfdheaderdir = $(pkgincludedir)/ospfd +ospfdheader_HEADERS = \ + ospfd/ospf_api.h \ + ospfd/ospf_asbr.h \ + ospfd/ospf_dump.h \ + ospfd/ospf_dump_api.h \ + ospfd/ospf_ism.h \ + ospfd/ospf_lsa.h \ + ospfd/ospf_lsdb.h \ + ospfd/ospf_nsm.h \ + ospfd/ospf_opaque.h \ + ospfd/ospfd.h \ + # end +endif + +clippy_scan += \ + ospfd/ospf_vty.c \ + ospfd/ospf_ldp_sync.c \ + ospfd/ospf_dump.c \ + ospfd/ospf_gr.c \ + # end + +noinst_HEADERS += \ + ospfd/ospf_abr.h \ + ospfd/ospf_apiserver.h \ + ospfd/ospf_ase.h \ + ospfd/ospf_bfd.h \ + ospfd/ospf_errors.h \ + ospfd/ospf_ext.h \ + ospfd/ospf_flood.h \ + ospfd/ospf_ia.h \ + ospfd/ospf_interface.h \ + ospfd/ospf_ldp_sync.h \ + ospfd/ospf_memory.h \ + ospfd/ospf_neighbor.h \ + ospfd/ospf_network.h \ + ospfd/ospf_packet.h \ + ospfd/ospf_ri.h \ + ospfd/ospf_gr.h \ + ospfd/ospf_route.h \ + ospfd/ospf_routemap_nb.h \ + ospfd/ospf_spf.h \ + ospfd/ospf_ti_lfa.h \ + ospfd/ospf_sr.h \ + ospfd/ospf_te.h \ + ospfd/ospf_vty.h \ + ospfd/ospf_zebra.h \ + ospfd/ospf_auth.h \ + # end + +ospfd_ospfd_LDADD = ospfd/libfrrospf.a ospfd/libfrrospfclient.a lib/libfrr.la $(LIBCAP) $(LIBM) +ospfd_ospfd_SOURCES = ospfd/ospf_main.c + +ospfd_ospfd_snmp_la_SOURCES = ospfd/ospf_snmp.c +ospfd_ospfd_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11 +ospfd_ospfd_snmp_la_LDFLAGS = $(MODULE_LDFLAGS) +ospfd_ospfd_snmp_la_LIBADD = lib/libfrrsnmp.la + +EXTRA_DIST += \ + ospfd/ChangeLog.opaque.txt \ + # end + +nodist_ospfd_ospfd_SOURCES = \ + yang/frr-ospf-route-map.yang.c \ + # end -- cgit v1.2.3