summaryrefslogtreecommitdiff
path: root/proto/ospf/topology.c
diff options
context:
space:
mode:
Diffstat (limited to 'proto/ospf/topology.c')
-rw-r--r--proto/ospf/topology.c2372
1 files changed, 1230 insertions, 1142 deletions
diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c
index 4af5afa5..0613d34d 100644
--- a/proto/ospf/topology.c
+++ b/proto/ospf/topology.c
@@ -3,6 +3,8 @@
*
* (c) 1999 Martin Mares <mj@ucw.cz>
* (c) 1999--2004 Ondrej Filip <feela@network.cz>
+ * (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
+ * (c) 2009--2014 CZ.NIC z.s.p.o.
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@@ -12,6 +14,7 @@
#include "ospf.h"
+
#define HASH_DEF_ORDER 6
#define HASH_HI_MARK *4
#define HASH_HI_STEP 2
@@ -20,166 +23,625 @@
#define HASH_LO_STEP 2
#define HASH_LO_MIN 8
-void originate_prefix_rt_lsa(struct ospf_area *oa);
-void originate_prefix_net_lsa(struct ospf_iface *ifa);
-void flush_prefix_net_lsa(struct ospf_iface *ifa);
+static inline void * lsab_flush(struct ospf_proto *p);
+static inline void lsab_reset(struct ospf_proto *p);
+
-#ifdef OSPFv2
-#define ipa_to_rid(x) _I(x)
-#else /* OSPFv3 */
-#define ipa_to_rid(x) _I3(x)
-#endif
+/**
+ * ospf_install_lsa - install new LSA into database
+ * @p: OSPF protocol instance
+ * @lsa: LSA header
+ * @type: type of LSA
+ * @domain: domain of LSA
+ * @body: pointer to LSA body
+ *
+ * This function ensures installing new LSA received in LS update into LSA
+ * database. Old instance is replaced. Several actions are taken to detect if
+ * new routing table calculation is necessary. This is described in 13.2 of RFC
+ * 2328. This function is for received LSA only, locally originated LSAs are
+ * installed by ospf_originate_lsa().
+ *
+ * The LSA body in @body is expected to be mb_allocated by the caller and its
+ * ownership is transferred to the LSA entry structure.
+ */
+struct top_hash_entry *
+ospf_install_lsa(struct ospf_proto *p, struct ospf_lsa_header *lsa, u32 type, u32 domain, void *body)
+{
+ struct top_hash_entry *en;
+ int change = 0;
+ en = ospf_hash_get(p->gr, domain, lsa->id, lsa->rt, type);
-#ifdef OSPFv2
-static inline u32
-fibnode_to_lsaid(struct proto_ospf *po, struct fib_node *fn)
-{
- /* We have to map IP prefixes to u32 in such manner that resulting
- u32 interpreted as IP address is a member of given
- prefix. Therefore, /32 prefix have to be mapped on itself.
- All received prefixes have to be mapped on different u32s.
-
- We have an assumption that if there is nontrivial (non-/32)
- network prefix, then there is not /32 prefix for the first
- and the last IP address of the network (these are usually
- reserved, therefore it is not an important restriction).
- The network prefix is mapped to the first or the last
- IP address in the manner that disallow collisions - we
- use IP address that cannot be used by parent prefix.
-
- For example:
- 192.168.0.0/24 maps to 192.168.0.255
- 192.168.1.0/24 maps to 192.168.1.0
- because 192.168.0.0 and 192.168.1.255 might be used by
- 192.168.0.0/23 .
-
- This is not compatible with older RFC 1583, so we have an option
- to the RFC 1583 compatible assignment (that always uses the first
- address) which disallows subnetting.
-
- Appendig E of RFC 2328 suggests different algorithm, that tries
- to maximize both compatibility and subnetting. But as it is not
- possible to have both reliably and the suggested algorithm was
- unnecessary complicated and it does crazy things like changing
- LSA ID for a network because different network appeared, we
- choose a different way. */
-
- u32 id = _I(fn->prefix);
-
- if ((po->rfc1583) || (fn->pxlen == 0) || (fn->pxlen == 32))
- return id;
+ if (!SNODE_VALID(en))
+ s_add_tail(&p->lsal, SNODE en);
- if (id & (1 << (32 - fn->pxlen)))
- return id;
+ if ((en->lsa_body == NULL) || /* No old LSA */
+ (en->lsa.length != lsa->length) ||
+ (en->lsa.type_raw != lsa->type_raw) || /* Check for OSPFv2 options */
+ (en->lsa.age == LSA_MAXAGE) ||
+ (lsa->age == LSA_MAXAGE) ||
+ memcmp(en->lsa_body, body, lsa->length - sizeof(struct ospf_lsa_header)))
+ change = 1;
+
+ if ((en->lsa.age == LSA_MAXAGE) && (lsa->age == LSA_MAXAGE))
+ change = 0;
+
+ mb_free(en->lsa_body);
+ en->lsa_body = body;
+ en->lsa = *lsa;
+ en->init_age = en->lsa.age;
+ en->inst_time = now;
+
+ /*
+ * We do not set en->mode. It is either default LSA_M_BASIC, or in a special
+ * case when en is local but flushed, there is postponed LSA, self-originated
+ * LSA is received and ospf_install_lsa() is called from ospf_advance_lse(),
+ * then we have en->mode from the postponed LSA origination.
+ */
+
+ OSPF_TRACE(D_EVENTS, "Installing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x, Age: %u",
+ en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn, en->lsa.age);
+
+ if (change)
+ ospf_schedule_rtcalc(p);
+
+ return en;
+}
+
+/**
+ * ospf_advance_lsa - handle received unexpected self-originated LSA
+ * @p: OSPF protocol instance
+ * @en: current LSA entry or NULL
+ * @lsa: new LSA header
+ * @type: type of LSA
+ * @domain: domain of LSA
+ * @body: pointer to LSA body
+ *
+ * This function handles received unexpected self-originated LSA (@lsa, @body)
+ * by either advancing sequence number of the local LSA instance (@en) and
+ * propagating it, or installing the received LSA and immediately flushing it
+ * (if there is no local LSA; i.e., @en is NULL or MaxAge).
+ *
+ * The LSA body in @body is expected to be mb_allocated by the caller and its
+ * ownership is transferred to the LSA entry structure or it is freed.
+ */
+void
+ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_lsa_header *lsa, u32 type, u32 domain, void *body)
+{
+ /* RFC 2328 13.4 */
+
+ if (en && (en->lsa.age < LSA_MAXAGE))
+ {
+ if (lsa->sn != LSA_MAXSEQNO)
+ {
+ /*
+ * We simply advance current LSA to have higher seqnum than received LSA.
+ * The received LSA is ignored and the advanced LSA is propagated instead.
+ *
+ * Although this is an origination of distinct LSA instance and therefore
+ * should be limited by MinLSInterval, we do not enforce it here. Fast
+ * reaction is needed and we are already limited by MinLSArrival.
+ */
+
+ mb_free(body);
+
+ en->lsa.sn = lsa->sn + 1;
+ en->lsa.age = 0;
+ en->init_age = 0;
+ en->inst_time = now;
+ lsasum_calculate(&en->lsa, en->lsa_body);
+
+ OSPF_TRACE(D_EVENTS, "Advancing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
+ en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn);
+ }
+ else
+ {
+ /*
+ * Received LSA has maximal sequence number, so we cannot simply override
+ * it. We have to install it to the database, immediately flush it to
+ * implement sequence number wrapping, and schedule our current LSA to be
+ * originated after the received instance is flushed.
+ */
+
+ if (en->next_lsa_body == NULL)
+ {
+ /* Schedule current LSA */
+ en->next_lsa_blen = en->lsa.length - sizeof(struct ospf_lsa_header);
+ en->next_lsa_body = en->lsa_body;
+ en->next_lsa_opts = ospf_is_v2(p) ? lsa_get_options(&en->lsa) : 0;
+ }
+ else
+ {
+ /* There is already scheduled LSA, so we just free current one */
+ mb_free(en->lsa_body);
+ }
+
+ en->lsa_body = body;
+ en->lsa = *lsa;
+ en->lsa.age = LSA_MAXAGE;
+ en->init_age = lsa->age;
+ en->inst_time = now;
+
+ OSPF_TRACE(D_EVENTS, "Resetting LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
+ en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn);
+ OSPF_TRACE(D_EVENTS, "Postponing LSA: Type: %04x, Id: %R, Rt: %R",
+ en->lsa_type, en->lsa.id, en->lsa.rt);
+ }
+ }
else
- return id | ~u32_mkmask(fn->pxlen);
+ {
+ /*
+ * We do not have received LSA in the database. We have to flush the
+ * received LSA. It has to be installed in the database to secure
+ * retransmissions. Note that the received LSA may already be MaxAge.
+ * Also note that en->next_lsa_* may be defined.
+ */
+
+ lsa->age = LSA_MAXAGE;
+ en = ospf_install_lsa(p, lsa, type, domain, body);
+ }
+
+ /*
+ * We flood the updated LSA. Although in some cases the to-be-flooded LSA is
+ * the same as the received LSA, and therefore we should propagate it as
+ * regular received LSA (send the acknowledgement instead of the update to
+ * the neighbor we received it from), we cheat a bit here.
+ */
+
+ ospf_flood_lsa(p, en, NULL);
}
-#else /* OSPFv3 */
-static inline u32
-fibnode_to_lsaid(struct proto_ospf *po, struct fib_node *fn)
+static int
+ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa_body, u16 lsa_blen, u16 lsa_opts)
{
+ /* Enforce MinLSInterval */
+ if ((en->init_age == 0) && en->inst_time && ((en->inst_time + MINLSINTERVAL) > now))
+ return 0;
+
+ /* Handle wrapping sequence number */
+ if (en->lsa.sn == LSA_MAXSEQNO)
+ {
+ /* Prepare to flush old LSA */
+ if (en->lsa.age != LSA_MAXAGE)
+ {
+ OSPF_TRACE(D_EVENTS, "Resetting LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
+ en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn);
+
+ en->lsa.age = LSA_MAXAGE;
+ ospf_flood_lsa(p, en, NULL);
+ return 0;
+ }
+
+ /* Already flushing */
+ if ((p->padj != 0) || (en->ret_count != 0))
+ return 0;
+
+ /* Flush done, just clean up seqnum, lsa_body is freed below */
+ en->lsa.sn = LSA_ZEROSEQNO;
+ }
+
/*
- * In OSPFv3, it is simpler. There is not a requirement for
- * membership of the result in the input network, so we just use a
- * hash-based unique ID of a routing table entry for a route that
- * originated given LSA. For ext-LSA, it is an imported route in the
- * nest's routing table (p->table). For summary-LSA, it is a
- * 'source' route in the protocol internal routing table (po->rtf).
+ * lsa.type_raw is initialized by ospf_hash_get() to OSPFv3 LSA type.
+ * lsa_set_options() implicitly converts it to OSPFv2 LSA type, assuming that
+ * old type is just new type masked by 0xff. That is not universally true,
+ * but it holds for all OSPFv2 types currently supported by BIRD.
*/
- return fn->uid;
+
+ if (ospf_is_v2(p))
+ lsa_set_options(&en->lsa, lsa_opts);
+
+ mb_free(en->lsa_body);
+ en->lsa_body = lsa_body;
+ en->lsa.length = sizeof(struct ospf_lsa_header) + lsa_blen;
+ en->lsa.sn++;
+ en->lsa.age = 0;
+ en->init_age = 0;
+ en->inst_time = now;
+ lsasum_calculate(&en->lsa, en->lsa_body);
+
+ OSPF_TRACE(D_EVENTS, "Originating LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
+ en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn);
+
+ ospf_flood_lsa(p, en, NULL);
+
+ if (en->mode == LSA_M_BASIC)
+ ospf_schedule_rtcalc(p);
+
+ return 1;
}
-#endif
+/**
+ * ospf_originate_lsa - originate new LSA
+ * @p: OSPF protocol instance
+ * @lsa: New LSA specification
+ *
+ * This function prepares a new LSA, installs it into the LSA database and
+ * floods it. If the new LSA cannot be originated now (because the old instance
+ * was originated within MinLSInterval, or because the LSA seqnum is currently
+ * wrapping), the origination is instead scheduled for later. If the new LSA is
+ * equivalent to the current LSA, the origination is skipped. In all cases, the
+ * corresponding LSA entry is returned. The new LSA is based on the LSA
+ * specification (@lsa) and the LSA body from lsab buffer of @p, which is
+ * emptied after the call. The opposite of this function is ospf_flush_lsa().
+ */
+struct top_hash_entry *
+ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa)
+{
+ struct top_hash_entry *en;
+ void *lsa_body = p->lsab;
+ u16 lsa_blen = p->lsab_used;
+ u16 lsa_length = sizeof(struct ospf_lsa_header) + lsa_blen;
+
+ en = ospf_hash_get(p->gr, lsa->dom, lsa->id, p->router_id, lsa->type);
+ if (!SNODE_VALID(en))
+ s_add_tail(&p->lsal, SNODE en);
-static void *
-lsab_alloc(struct proto_ospf *po, unsigned size)
+ if (en->lsa_body == NULL)
+ en->nf = lsa->nf;
+
+ if (en->nf != lsa->nf)
+ {
+ log(L_ERR "%s: LSA ID collision for %I/%d",
+ p->p.name, lsa->nf->fn.prefix, lsa->nf->fn.pxlen);
+
+ en = NULL;
+ goto drop;
+ }
+
+ if (en->mode != lsa->mode)
+ en->mode = lsa->mode;
+
+ if (en->next_lsa_body)
+ {
+ /* Ignore the new LSA if it is the same as the scheduled one */
+ if ((lsa_blen == en->next_lsa_blen) &&
+ !memcmp(lsa_body, en->next_lsa_body, lsa_blen) &&
+ (!ospf_is_v2(p) || (lsa->opts == en->next_lsa_opts)))
+ goto drop;
+
+ /* Free scheduled LSA */
+ mb_free(en->next_lsa_body);
+ en->next_lsa_body = NULL;
+ en->next_lsa_blen = 0;
+ en->next_lsa_opts = 0;
+ }
+
+ /* Ignore the the new LSA if is the same as the current one */
+ if ((en->lsa.age < LSA_MAXAGE) &&
+ (lsa_length == en->lsa.length) &&
+ !memcmp(lsa_body, en->lsa_body, lsa_blen) &&
+ (!ospf_is_v2(p) || (lsa->opts == lsa_get_options(&en->lsa))))
+ goto drop;
+
+ lsa_body = lsab_flush(p);
+
+ if (! ospf_do_originate_lsa(p, en, lsa_body, lsa_blen, lsa->opts))
+ {
+ OSPF_TRACE(D_EVENTS, "Postponing LSA: Type: %04x, Id: %R, Rt: %R",
+ en->lsa_type, en->lsa.id, en->lsa.rt);
+
+ en->next_lsa_body = lsa_body;
+ en->next_lsa_blen = lsa_blen;
+ en->next_lsa_opts = lsa->opts;
+ }
+
+ return en;
+
+ drop:
+ lsab_reset(p);
+ return en;
+}
+
+static void
+ospf_originate_next_lsa(struct ospf_proto *p, struct top_hash_entry *en)
+{
+ /* Called by ospf_update_lsadb() to handle scheduled origination */
+
+ if (! ospf_do_originate_lsa(p, en, en->next_lsa_body, en->next_lsa_blen, en->next_lsa_opts))
+ return;
+
+ en->next_lsa_body = NULL;
+ en->next_lsa_blen = 0;
+ en->next_lsa_opts = 0;
+}
+
+static void
+ospf_refresh_lsa(struct ospf_proto *p, struct top_hash_entry *en)
+{
+ /*
+ * Called by ospf_update_lsadb() for periodic LSA refresh.
+ *
+ * We know that lsa.age < LSA_MAXAGE and lsa.rt is our router ID. We can also
+ * assume that there is no scheduled LSA, because inst_time is deep in past,
+ * therefore ospf_originate_next_lsa() called before would either succeed or
+ * switched lsa.age to LSA_MAXAGE.
+ */
+
+ OSPF_TRACE(D_EVENTS, "Refreshing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
+ en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn);
+
+ ASSERT(en->next_lsa_body == NULL);
+
+ /* Handle wrapping sequence number */
+ if (en->lsa.sn == LSA_MAXSEQNO)
+ {
+ /* Copy LSA body as next LSA to get automatic origination after flush is finished */
+ en->next_lsa_blen = en->lsa.length - sizeof(struct ospf_lsa_header);
+ en->next_lsa_body = mb_alloc(p->p.pool, en->next_lsa_blen);
+ memcpy(en->next_lsa_body, en->lsa_body, en->next_lsa_blen);
+ en->next_lsa_opts = ospf_is_v2(p) ? lsa_get_options(&en->lsa) : 0;
+
+ en->lsa.age = LSA_MAXAGE;
+ ospf_flood_lsa(p, en, NULL);
+ return;
+ }
+
+ en->lsa.sn++;
+ en->lsa.age = 0;
+ en->init_age = 0;
+ en->inst_time = now;
+ lsasum_calculate(&en->lsa, en->lsa_body);
+ ospf_flood_lsa(p, en, NULL);
+}
+
+/**
+ * ospf_flush_lsa - flush LSA from OSPF domain
+ * @p: OSPF protocol instance
+ * @en: LSA entry to flush
+ *
+ * This function flushes @en from the OSPF domain by setting its age to
+ * %LSA_MAXAGE and flooding it. That also triggers subsequent events in LSA
+ * lifecycle leading to removal of the LSA from the LSA database (e.g. the LSA
+ * content is freed when flushing is acknowledged by neighbors). The function
+ * does nothing if the LSA is already being flushed. LSA entries are not
+ * immediately removed when being flushed, the caller may assume that @en still
+ * exists after the call. The function is the opposite of ospf_originate_lsa()
+ * and is supposed to do the right thing even in cases of postponed
+ * origination.
+ */
+void
+ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en)
{
- unsigned offset = po->lsab_used;
- po->lsab_used += size;
- if (po->lsab_used > po->lsab_size)
+ if (en->next_lsa_body)
+ {
+ mb_free(en->next_lsa_body);
+ en->next_lsa_body = NULL;
+ en->next_lsa_blen = 0;
+ en->next_lsa_opts = 0;
+ }
+
+ if (en->lsa.age == LSA_MAXAGE)
+ return;
+
+ OSPF_TRACE(D_EVENTS, "Flushing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
+ en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn);
+
+ en->lsa.age = LSA_MAXAGE;
+ ospf_flood_lsa(p, en, NULL);
+
+ if (en->mode == LSA_M_BASIC)
+ ospf_schedule_rtcalc(p);
+
+ en->mode = LSA_M_BASIC;
+}
+
+static void
+ospf_clear_lsa(struct ospf_proto *p, struct top_hash_entry *en)
+{
+ /*
+ * Called by ospf_update_lsadb() as part of LSA flushing process.
+ * Flushed LSA was acknowledged by neighbors and we can free its content.
+ * The log message is for 'remove' - we hide empty LSAs from users.
+ */
+
+ OSPF_TRACE(D_EVENTS, "Removing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
+ en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn);
+
+ if (en->lsa.sn == LSA_MAXSEQNO)
+ en->lsa.sn = LSA_ZEROSEQNO;
+
+ mb_free(en->lsa_body);
+ en->lsa_body = NULL;
+}
+
+static void
+ospf_remove_lsa(struct ospf_proto *p, struct top_hash_entry *en)
+{
+ /*
+ * Called by ospf_update_lsadb() as part of LSA flushing process.
+ * Both lsa_body and next_lsa_body are NULL.
+ */
+
+ s_rem_node(SNODE en);
+ ospf_hash_delete(p->gr, en);
+}
+
+/**
+ * ospf_update_lsadb - update LSA database
+ * @p: OSPF protocol instance
+ *
+ * This function is periodicaly invoked from ospf_disp(). It does some periodic
+ * or postponed processing related to LSA entries. It originates postponed LSAs
+ * scheduled by ospf_originate_lsa(), It continues in flushing processes started
+ * by ospf_flush_lsa(). It also periodically refreshs locally originated LSAs --
+ * when the current instance is older %LSREFRESHTIME, a new instance is originated.
+ * Finally, it also ages stored LSAs and flushes ones that reached %LSA_MAXAGE.
+ *
+ * The RFC 2328 says that a router should periodically check checksums of all
+ * stored LSAs to detect hardware problems. This is not implemented.
+ */
+void
+ospf_update_lsadb(struct ospf_proto *p)
+{
+ struct top_hash_entry *en, *nxt;
+ bird_clock_t real_age;
+
+ WALK_SLIST_DELSAFE(en, nxt, p->lsal)
+ {
+ if (en->next_lsa_body)
+ ospf_originate_next_lsa(p, en);
+
+ real_age = en->init_age + (now - en->inst_time);
+
+ if (en->lsa.age == LSA_MAXAGE)
+ {
+ if (en->lsa_body && (p->padj == 0) && (en->ret_count == 0))
+ ospf_clear_lsa(p, en);
+
+ if ((en->lsa_body == NULL) && (en->next_lsa_body == NULL) &&
+ ((en->lsa.rt != p->router_id) || (real_age >= LSA_MAXAGE)))
+ ospf_remove_lsa(p, en);
+
+ continue;
+ }
+
+ if ((en->lsa.rt == p->router_id) && (real_age >= LSREFRESHTIME))
+ {
+ ospf_refresh_lsa(p, en);
+ continue;
+ }
+
+ if (real_age >= LSA_MAXAGE)
{
- po->lsab_size = MAX(po->lsab_used, 2 * po->lsab_size);
- po->lsab = po->lsab ? mb_realloc(po->lsab, po->lsab_size):
- mb_alloc(po->proto.pool, po->lsab_size);
+ ospf_flush_lsa(p, en);
+ continue;
}
- return ((byte *) po->lsab) + offset;
+
+ en->lsa.age = real_age;
+ }
+}
+
+
+static inline u32
+ort_to_lsaid(struct ospf_proto *p, ort *nf)
+{
+ /*
+ * In OSPFv2, We have to map IP prefixes to u32 in such manner that resulting
+ * u32 interpreted as IP address is a member of given prefix. Therefore, /32
+ * prefix have to be mapped on itself. All received prefixes have to be
+ * mapped on different u32s.
+ *
+ * We have an assumption that if there is nontrivial (non-/32) network prefix,
+ * then there is not /32 prefix for the first and the last IP address of the
+ * network (these are usually reserved, therefore it is not an important
+ * restriction). The network prefix is mapped to the first or the last IP
+ * address in the manner that disallow collisions - we use the IP address that
+ * cannot be used by the parent prefix.
+ *
+ * For example:
+ * 192.168.0.0/24 maps to 192.168.0.255
+ * 192.168.1.0/24 maps to 192.168.1.0
+ * because 192.168.0.0 and 192.168.1.255 might be used by 192.168.0.0/23 .
+ *
+ * Appendig E of RFC 2328 suggests different algorithm, that tries to maximize
+ * both compatibility and subnetting. But as it is not possible to have both
+ * reliably and the suggested algorithm was unnecessary complicated and it
+ * does crazy things like changing LSA ID for a network because different
+ * network appeared, we choose a different way.
+ *
+ * In OSPFv3, it is simpler. There is not a requirement for membership of the
+ * result in the input network, so we just use a hash-based unique ID of a
+ * routing table entry for a route that originated given LSA. For ext-LSA, it
+ * is an imported route in the nest's routing table (p->table). For summary-LSA,
+ * it is a 'source' route in the protocol internal routing table (p->rtf).
+ */
+
+ if (ospf_is_v3(p))
+ return nf->fn.uid;
+
+ u32 id = ipa_to_u32(nf->fn.prefix);
+ int pxlen = nf->fn.pxlen;
+
+ if ((pxlen == 0) || (pxlen == 32))
+ return id;
+
+ if (id & (1 << (32 - pxlen)))
+ return id;
+ else
+ return id | ~u32_mkmask(pxlen);
+}
+
+
+static void *
+lsab_alloc(struct ospf_proto *p, uint size)
+{
+ uint offset = p->lsab_used;
+ p->lsab_used += size;
+ if (p->lsab_used > p->lsab_size)
+ {
+ p->lsab_size = MAX(p->lsab_used, 2 * p->lsab_size);
+ p->lsab = p->lsab ? mb_realloc(p->lsab, p->lsab_size):
+ mb_alloc(p->p.pool, p->lsab_size);
+ }
+ return ((byte *) p->lsab) + offset;
}
static inline void *
-lsab_allocz(struct proto_ospf *po, unsigned size)
+lsab_allocz(struct ospf_proto *p, uint size)
{
- void *r = lsab_alloc(po, size);
+ void *r = lsab_alloc(p, size);
bzero(r, size);
return r;
}
static inline void *
-lsab_flush(struct proto_ospf *po)
+lsab_flush(struct ospf_proto *p)
{
- void *r = mb_alloc(po->proto.pool, po->lsab_used);
- memcpy(r, po->lsab, po->lsab_used);
- po->lsab_used = 0;
+ void *r = mb_alloc(p->p.pool, p->lsab_used);
+ memcpy(r, p->lsab, p->lsab_used);
+ p->lsab_used = 0;
return r;
}
-static inline void *
-lsab_offset(struct proto_ospf *po, unsigned offset)
+static inline void
+lsab_reset(struct ospf_proto *p)
{
- return ((byte *) po->lsab) + offset;
+ p->lsab_used = 0;
}
static inline void *
-lsab_end(struct proto_ospf *po)
+lsab_offset(struct ospf_proto *p, uint offset)
{
- return ((byte *) po->lsab) + po->lsab_used;
+ return ((byte *) p->lsab) + offset;
}
-static s32
-get_seqnum(struct top_hash_entry *en)
+static inline void *
+lsab_end(struct ospf_proto *p)
{
- if (!en)
- return LSA_INITSEQNO;
-
- if (en->lsa.sn == LSA_MAXSEQNO)
- {
- log(L_WARN "OSPF: Premature origination of LSA (Type: %04x, Id: %R, Rt: %R)",
- en->lsa.type, en->lsa.id, en->lsa.rt);
- return LSA_INITSEQNO;
- }
-
- return en->lsa.sn + 1;
+ return ((byte *) p->lsab) + p->lsab_used;
}
+/*
+ * Router-LSA handling
+ * Type = LSA_T_RT
+ */
+
static int
configured_stubnet(struct ospf_area *oa, struct ifa *a)
{
- if (!oa->ac)
- return 0;
-
/* Does not work for IA_PEER addresses, but it is not called on these */
struct ospf_stubnet_config *sn;
WALK_LIST(sn, oa->ac->stubnet_list)
+ {
+ if (sn->summary)
{
- if (sn->summary)
- {
- if (ipa_in_net(a->prefix, sn->px.addr, sn->px.len) && (a->pxlen >= sn->px.len))
- return 1;
- }
- else
- {
- if (ipa_equal(a->prefix, sn->px.addr) && (a->pxlen == sn->px.len))
- return 1;
- }
+ if (ipa_in_net(a->prefix, sn->px.addr, sn->px.len) && (a->pxlen >= sn->px.len))
+ return 1;
}
+ else
+ {
+ if (ipa_equal(a->prefix, sn->px.addr) && (a->pxlen == sn->px.len))
+ return 1;
+ }
+ }
+
return 0;
}
-int
+static int
bcast_net_active(struct ospf_iface *ifa)
{
struct ospf_neighbor *neigh;
@@ -188,53 +650,66 @@ bcast_net_active(struct ospf_iface *ifa)
return 0;
WALK_LIST(neigh, ifa->neigh_list)
+ {
+ if (neigh->state == NEIGHBOR_FULL)
{
- if (neigh->state == NEIGHBOR_FULL)
- {
- if (neigh->rid == ifa->drid)
- return 1;
+ if (neigh->rid == ifa->drid)
+ return 1;
- if (ifa->state == OSPF_IS_DR)
- return 1;
- }
+ if (ifa->state == OSPF_IS_DR)
+ return 1;
}
+ }
return 0;
}
+static inline u32
+get_rt_options(struct ospf_proto *p, struct ospf_area *oa, int bitv)
+{
+ u32 opts = 0;
-#ifdef OSPFv2
+ if (p->areano > 1)
+ opts |= OPT_RT_B;
-static void *
-originate_rt_lsa_body(struct ospf_area *oa, u16 *length)
-{
- struct proto_ospf *po = oa->po;
- struct ospf_iface *ifa;
- int i = 0, bitv = 0;
- struct ospf_lsa_rt *rt;
- struct ospf_lsa_rt_link *ln;
- struct ospf_neighbor *neigh;
+ if ((p->areano > 1) && oa_is_nssa(oa) && oa->ac->translator)
+ opts |= OPT_RT_NT;
- ASSERT(po->lsab_used == 0);
- rt = lsab_allocz(po, sizeof(struct ospf_lsa_rt));
+ if (p->asbr && !oa_is_stub(oa))
+ opts |= OPT_RT_E;
- rt->options = 0;
+ if (bitv)
+ opts |= OPT_RT_V;
- if (po->areano > 1)
- rt->options |= OPT_RT_B;
+ return opts;
+}
- if ((po->areano > 1) && oa_is_nssa(oa) && oa->ac->translator)
- rt->options |= OPT_RT_NT;
+static inline void
+add_rt2_lsa_link(struct ospf_proto *p, u8 type, u32 id, u32 data, u16 metric)
+{
+ struct ospf_lsa_rt2_link *ln = lsab_alloc(p, sizeof(struct ospf_lsa_rt2_link));
+ ln->type = type;
+ ln->id = id;
+ ln->data = data;
+ ln->metric = metric;
+ ln->no_tos = 0;
+}
- if (po->ebit && !oa_is_stub(oa))
- rt->options |= OPT_RT_E;
+static void
+prepare_rt2_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
+{
+ struct ospf_iface *ifa;
+ int i = 0, bitv = 0;
+ struct ospf_neighbor *neigh;
- rt = NULL; /* buffer might be reallocated later */
+ ASSERT(p->lsab_used == 0);
+ lsab_allocz(p, sizeof(struct ospf_lsa_rt));
+ /* ospf_lsa_rt header will be filled later */
- WALK_LIST(ifa, po->iface_list)
+ WALK_LIST(ifa, p->iface_list)
{
int net_lsa = 0;
- u32 link_cost = po->stub_router ? 0xffff : ifa->cost;
+ u32 link_cost = p->stub_router ? 0xffff : ifa->cost;
if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa == oa) &&
(!EMPTY_LIST(ifa->neigh_list)))
@@ -249,66 +724,47 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length)
ifa->rt_pos_beg = i;
- /* RFC2328 - 12.4.1.1-4 */
+ /* RFC 2328 - 12.4.1.1-4 */
switch (ifa->type)
- {
- case OSPF_IT_PTP:
- case OSPF_IT_PTMP:
- WALK_LIST(neigh, ifa->neigh_list)
- if (neigh->state == NEIGHBOR_FULL)
- {
- ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link));
- ln->type = LSART_PTP;
- ln->id = neigh->rid;
-
- /*
- * ln->data should be ifa->iface_id in case of no/ptp
- * address (ifa->addr->flags & IA_PEER) on PTP link (see
- * RFC 2328 12.4.1.1.), but the iface ID value has no use,
- * while using IP address even in this case is here for
- * compatibility with some broken implementations that use
- * this address as a next-hop.
- */
- ln->data = ipa_to_u32(ifa->addr->ip);
- ln->metric = link_cost;
- ln->padding = 0;
- i++;
- }
- break;
-
- case OSPF_IT_BCAST:
- case OSPF_IT_NBMA:
- if (bcast_net_active(ifa))
- {
- ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link));
- ln->type = LSART_NET;
- ln->id = ipa_to_u32(ifa->drip);
- ln->data = ipa_to_u32(ifa->addr->ip);
- ln->metric = link_cost;
- ln->padding = 0;
- i++;
- net_lsa = 1;
- }
- break;
-
- case OSPF_IT_VLINK:
- neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list);
- if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff))
+ {
+ case OSPF_IT_PTP:
+ case OSPF_IT_PTMP:
+ WALK_LIST(neigh, ifa->neigh_list)
+ if (neigh->state == NEIGHBOR_FULL)
{
- ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link));
- ln->type = LSART_VLNK;
- ln->id = neigh->rid;
- ln->data = ipa_to_u32(ifa->addr->ip);
- ln->metric = link_cost;
- ln->padding = 0;
+ /*
+ * ln->data should be ifa->iface_id in case of no/ptp
+ * address (ifa->addr->flags & IA_PEER) on PTP link (see
+ * RFC 2328 12.4.1.1.), but the iface ID value has no use,
+ * while using IP address even in this case is here for
+ * compatibility with some broken implementations that use
+ * this address as a next-hop.
+ */
+ add_rt2_lsa_link(p, LSART_PTP, neigh->rid, ipa_to_u32(ifa->addr->ip), link_cost);
i++;
- }
- break;
+ }
+ break;
- default:
- log("Unknown interface type %s", ifa->ifname);
- break;
+ case OSPF_IT_BCAST:
+ case OSPF_IT_NBMA:
+ if (bcast_net_active(ifa))
+ {
+ add_rt2_lsa_link(p, LSART_NET, ipa_to_u32(ifa->drip), ipa_to_u32(ifa->addr->ip), link_cost);
+ i++;
+ net_lsa = 1;
}
+ break;
+
+ case OSPF_IT_VLINK:
+ neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list);
+ if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff))
+ add_rt2_lsa_link(p, LSART_VLNK, neigh->rid, ipa_to_u32(ifa->addr->ip), link_cost), i++;
+ break;
+
+ default:
+ log(L_BUG "OSPF: Unknown interface type");
+ break;
+ }
ifa->rt_pos_end = i;
@@ -319,62 +775,32 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length)
configured_stubnet(oa, ifa->addr))
continue;
- ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link));
+ /* Host or network stub entry */
if ((ifa->addr->flags & IA_HOST) ||
(ifa->state == OSPF_IS_LOOP) ||
(ifa->type == OSPF_IT_PTMP))
- {
- /* Host stub entry */
- ln->type = LSART_STUB;
- ln->id = ipa_to_u32(ifa->addr->ip);
- ln->data = 0xffffffff;
- ln->metric = 0;
- ln->padding = 0;
- }
- else
- {
- /* Network stub entry */
- ln->type = LSART_STUB;
- ln->id = ipa_to_u32(ifa->addr->prefix);
- ln->data = ipa_to_u32(ipa_mkmask(ifa->addr->pxlen));
- ln->metric = ifa->cost;
- ln->padding = 0;
- }
+ add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(ifa->addr->ip), 0xffffffff, 0);
+ else
+ add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(ifa->addr->prefix), u32_mkmask(ifa->addr->pxlen), ifa->cost);
i++;
ifa->rt_pos_end = i;
}
struct ospf_stubnet_config *sn;
- if (oa->ac)
- WALK_LIST(sn, oa->ac->stubnet_list)
- if (!sn->hidden)
- {
- ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link));
- ln->type = LSART_STUB;
- ln->id = ipa_to_u32(sn->px.addr);
- ln->data = ipa_to_u32(ipa_mkmask(sn->px.len));
- ln->metric = sn->cost;
- ln->padding = 0;
- i++;
- }
-
- rt = po->lsab;
- rt->links = i;
-
- if (bitv)
- rt->options |= OPT_RT_V;
+ WALK_LIST(sn, oa->ac->stubnet_list)
+ if (!sn->hidden)
+ add_rt2_lsa_link(p, LSART_STUB, ipa_to_u32(sn->px.addr), u32_mkmask(sn->px.len), sn->cost), i++;
- *length = po->lsab_used + sizeof(struct ospf_lsa_header);
- return lsab_flush(po);
+ struct ospf_lsa_rt *rt = p->lsab;
+ /* Store number of links in lower half of options */
+ rt->options = get_rt_options(p, oa, bitv) | (u16) i;
}
-#else /* OSPFv3 */
-
-static void
-add_lsa_rt_link(struct proto_ospf *po, struct ospf_iface *ifa, u8 type, u32 nif, u32 id)
+static inline void
+add_rt3_lsa_link(struct ospf_proto *p, u8 type, struct ospf_iface *ifa, u32 nif, u32 id)
{
- struct ospf_lsa_rt_link *ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link));
+ struct ospf_lsa_rt3_link *ln = lsab_alloc(p, sizeof(struct ospf_lsa_rt3_link));
ln->type = type;
ln->padding = 0;
ln->metric = ifa->cost;
@@ -383,33 +809,19 @@ add_lsa_rt_link(struct proto_ospf *po, struct ospf_iface *ifa, u8 type, u32 nif,
ln->id = id;
}
-static void *
-originate_rt_lsa_body(struct ospf_area *oa, u16 *length)
+static void
+prepare_rt3_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
{
- struct proto_ospf *po = oa->po;
struct ospf_iface *ifa;
+ struct ospf_neighbor *neigh;
int bitv = 0;
int i = 0;
- struct ospf_lsa_rt *rt;
- struct ospf_neighbor *neigh;
-
- ASSERT(po->lsab_used == 0);
- rt = lsab_allocz(po, sizeof(struct ospf_lsa_rt));
- rt->options = oa->options & OPTIONS_MASK;
+ ASSERT(p->lsab_used == 0);
+ lsab_allocz(p, sizeof(struct ospf_lsa_rt));
+ /* ospf_lsa_rt header will be filled later */
- if (po->areano > 1)
- rt->options |= OPT_RT_B;
-
- if ((po->areano > 1) && oa_is_nssa(oa) && oa->ac->translator)
- rt->options |= OPT_RT_NT;
-
- if (po->ebit && !oa_is_stub(oa))
- rt->options |= OPT_RT_E;
-
- rt = NULL; /* buffer might be reallocated later */
-
- WALK_LIST(ifa, po->iface_list)
+ WALK_LIST(ifa, p->iface_list)
{
if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa == oa) &&
(!EMPTY_LIST(ifa->neigh_list)))
@@ -424,854 +836,556 @@ originate_rt_lsa_body(struct ospf_area *oa, u16 *length)
ifa->rt_pos_beg = i;
- /* RFC5340 - 4.4.3.2 */
+ /* RFC 5340 - 4.4.3.2 */
switch (ifa->type)
- {
- case OSPF_IT_PTP:
- case OSPF_IT_PTMP:
- WALK_LIST(neigh, ifa->neigh_list)
- if (neigh->state == NEIGHBOR_FULL)
- add_lsa_rt_link(po, ifa, LSART_PTP, neigh->iface_id, neigh->rid), i++;
- break;
-
- case OSPF_IT_BCAST:
- case OSPF_IT_NBMA:
- if (bcast_net_active(ifa))
- add_lsa_rt_link(po, ifa, LSART_NET, ifa->dr_iface_id, ifa->drid), i++;
- break;
-
- case OSPF_IT_VLINK:
- neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list);
- if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff))
- add_lsa_rt_link(po, ifa, LSART_VLNK, neigh->iface_id, neigh->rid), i++;
- break;
-
- default:
- log("Unknown interface type %s", ifa->ifname);
- break;
- }
-
- ifa->rt_pos_end = i;
- }
-
- if (bitv)
{
- rt = po->lsab;
- rt->options |= OPT_RT_V;
- }
-
- *length = po->lsab_used + sizeof(struct ospf_lsa_header);
- return lsab_flush(po);
-}
-
-#endif
+ case OSPF_IT_PTP:
+ case OSPF_IT_PTMP:
+ WALK_LIST(neigh, ifa->neigh_list)
+ if (neigh->state == NEIGHBOR_FULL)
+ add_rt3_lsa_link(p, LSART_PTP, ifa, neigh->iface_id, neigh->rid), i++;
+ break;
-/**
- * originate_rt_lsa - build new instance of router LSA
- * @oa: ospf_area which is LSA built to
- *
- * It builds router LSA walking through all OSPF interfaces in
- * specified OSPF area. This function is mostly called from
- * area_disp(). Builds new LSA, increases sequence number (if old
- * instance exists) and sets age of LSA to zero.
- */
-void
-originate_rt_lsa(struct ospf_area *oa)
-{
- struct ospf_lsa_header lsa;
- struct proto_ospf *po = oa->po;
- struct proto *p = &po->proto;
- void *body;
+ case OSPF_IT_BCAST:
+ case OSPF_IT_NBMA:
+ if (bcast_net_active(ifa))
+ add_rt3_lsa_link(p, LSART_NET, ifa, ifa->dr_iface_id, ifa->drid), i++;
+ break;
- OSPF_TRACE(D_EVENTS, "Originating router-LSA for area %R", oa->areaid);
+ case OSPF_IT_VLINK:
+ neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list);
+ if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff))
+ add_rt3_lsa_link(p, LSART_VLNK, ifa, neigh->iface_id, neigh->rid), i++;
+ break;
- lsa.age = 0;
- lsa.type = LSA_T_RT;
-
-#ifdef OSPFv2
- lsa.options = oa->options;
- lsa.id = po->router_id;
-#else /* OSPFv3 */
- lsa.id = 0;
-#endif
+ default:
+ log(L_BUG "OSPF: Unknown interface type");
+ break;
+ }
- lsa.rt = po->router_id;
- lsa.sn = get_seqnum(oa->rt);
- u32 dom = oa->areaid;
+ ifa->rt_pos_end = i;
+ }
- body = originate_rt_lsa_body(oa, &lsa.length);
- lsasum_calculate(&lsa, body);
- oa->rt = lsa_install_new(po, &lsa, dom, body);
- ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
+ struct ospf_lsa_rt *rt = p->lsab;
+ rt->options = get_rt_options(p, oa, bitv) | (oa->options & LSA_OPTIONS_MASK);
}
-void
-update_rt_lsa(struct ospf_area *oa)
+static void
+ospf_originate_rt_lsa(struct ospf_proto *p, struct ospf_area *oa)
{
- struct proto_ospf *po = oa->po;
+ struct ospf_new_lsa lsa = {
+ .type = LSA_T_RT,
+ .dom = oa->areaid,
+ .id = ospf_is_v2(p) ? p->router_id : 0,
+ .opts = oa->options
+ };
- if ((oa->rt) && ((oa->rt->inst_t + MINLSINTERVAL)) > now)
- return;
- /*
- * Tick is probably set to very low value. We cannot
- * originate new LSA before MINLSINTERVAL. We will
- * try to do it next tick.
- */
+ OSPF_TRACE(D_EVENTS, "Updating router state for area %R", oa->areaid);
- originate_rt_lsa(oa);
-#ifdef OSPFv3
- originate_prefix_rt_lsa(oa);
-#endif
+ if (ospf_is_v2(p))
+ prepare_rt2_lsa_body(p, oa);
+ else
+ prepare_rt3_lsa_body(p, oa);
- schedule_rtcalc(po);
- oa->origrt = 0;
+ oa->rt = ospf_originate_lsa(p, &lsa);
}
-static void *
-originate_net_lsa_body(struct ospf_iface *ifa, u16 *length,
- struct proto_ospf *po)
+
+/*
+ * Net-LSA handling
+ * Type = LSA_T_NET
+ */
+
+static void
+prepare_net2_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
{
- u16 i = 1;
- struct ospf_neighbor *n;
struct ospf_lsa_net *net;
+ struct ospf_neighbor *n;
int nodes = ifa->fadj + 1;
+ u16 i = 1;
- net = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_net)
- + nodes * sizeof(u32));
-
-#ifdef OSPFv2
- net->netmask = ipa_mkmask(ifa->addr->pxlen);
-#endif
-
-#ifdef OSPFv3
- /* In OSPFv3, we would like to merge options from Link LSAs of added neighbors */
- struct top_hash_entry *en;
- u32 options = 0;
-#endif
+ ASSERT(p->lsab_used == 0);
+ net = lsab_alloc(p, sizeof(struct ospf_lsa_net) + 4 * nodes);
- net->routers[0] = po->router_id;
+ net->optx = u32_mkmask(ifa->addr->pxlen);
+ net->routers[0] = p->router_id;
WALK_LIST(n, ifa->neigh_list)
{
if (n->state == NEIGHBOR_FULL)
{
-#ifdef OSPFv3
- en = ospf_hash_find(po->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK);
- if (en)
- options |= ((struct ospf_lsa_link *) en->lsa_body)->options;
-#endif
-
net->routers[i] = n->rid;
i++;
}
}
ASSERT(i == nodes);
-
-#ifdef OSPFv3
- net->options = options & OPTIONS_MASK;
-#endif
-
- *length = sizeof(struct ospf_lsa_header) + sizeof(struct ospf_lsa_net)
- + nodes * sizeof(u32);
- return net;
}
-
-/**
- * originate_net_lsa - originates of deletes network LSA
- * @ifa: interface which is LSA originated for
- *
- * Interface counts number of adjacent neighbors. If this number is
- * lower than one or interface is not in state %OSPF_IS_DR it deletes
- * and premature ages instance of network LSA for specified interface.
- * In other case, new instance of network LSA is originated.
- */
-void
-originate_net_lsa(struct ospf_iface *ifa)
+static void
+prepare_net3_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
{
- struct proto_ospf *po = ifa->oa->po;
- struct proto *p = &po->proto;
- struct ospf_lsa_header lsa;
- u32 dom = ifa->oa->areaid;
-
- void *body;
-
- OSPF_TRACE(D_EVENTS, "Originating network-LSA for iface %s", ifa->ifname);
-
- lsa.age = 0;
- lsa.type = LSA_T_NET;
-
-#ifdef OSPFv2
- lsa.options = ifa->oa->options;
- lsa.id = ipa_to_u32(ifa->addr->ip);
-#else /* OSPFv3 */
- lsa.id = ifa->iface_id;
-#endif
+ struct ospf_lsa_net *net;
+ int nodes = ifa->fadj + 1;
+ u32 options = 0;
+ u16 i = 1;
- lsa.rt = po->router_id;
- lsa.sn = get_seqnum(ifa->net_lsa);
+ ASSERT(p->lsab_used == 0);
+ net = lsab_alloc(p, sizeof(struct ospf_lsa_net) + 4 * nodes);
- body = originate_net_lsa_body(ifa, &lsa.length, po);
- lsasum_calculate(&lsa, body);
- ifa->net_lsa = lsa_install_new(po, &lsa, dom, body);
- ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
-}
+ net->routers[0] = p->router_id;
-void
-flush_net_lsa(struct ospf_iface *ifa)
-{
- struct proto_ospf *po = ifa->oa->po;
- struct proto *p = &po->proto;
- u32 dom = ifa->oa->areaid;
-
- if (ifa->net_lsa == NULL)
- return;
+ struct ospf_neighbor *n;
+ WALK_LIST(n, ifa->neigh_list)
+ {
+ if (n->state == NEIGHBOR_FULL)
+ {
+ /* In OSPFv3, we would like to merge options from Link LSAs of added neighbors */
- OSPF_TRACE(D_EVENTS, "Flushing network-LSA for iface %s", ifa->ifname);
- ifa->net_lsa->lsa.sn += 1;
- ifa->net_lsa->lsa.age = LSA_MAXAGE;
- lsasum_calculate(&ifa->net_lsa->lsa, ifa->net_lsa->lsa_body);
- ospf_lsupd_flood(po, NULL, NULL, &ifa->net_lsa->lsa, dom, 0);
- flush_lsa(ifa->net_lsa, po);
- ifa->net_lsa = NULL;
-}
+ struct top_hash_entry *en =
+ ospf_hash_find(p->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK);
-void
-update_net_lsa(struct ospf_iface *ifa)
-{
- struct proto_ospf *po = ifa->oa->po;
-
- if (ifa->net_lsa && ((ifa->net_lsa->inst_t + MINLSINTERVAL) > now))
- return;
- /*
- * It's too early to originate new network LSA. We will
- * try to do it next tick
- */
+ if (en)
+ options |= ((struct ospf_lsa_link *) en->lsa_body)->options;
- if ((ifa->state != OSPF_IS_DR) || (ifa->fadj == 0))
- {
- flush_net_lsa(ifa);
-#ifdef OSPFv3
- flush_prefix_net_lsa(ifa);
-#endif
- }
- else
- {
- originate_net_lsa(ifa);
-#ifdef OSPFv3
- originate_prefix_net_lsa(ifa);
-#endif
+ net->routers[i] = n->rid;
+ i++;
}
+ }
+ ASSERT(i == nodes);
- schedule_rtcalc(po);
- ifa->orignet = 0;
+ net->optx = options & LSA_OPTIONS_MASK;
}
-#ifdef OSPFv2
-
-static inline void *
-originate_sum_lsa_body(struct proto_ospf *po, u16 *length, u32 mlen, u32 metric)
+static void
+ospf_originate_net_lsa(struct ospf_proto *p, struct ospf_iface *ifa)
{
- struct ospf_lsa_sum *sum = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_sum));
- *length = sizeof(struct ospf_lsa_header) + sizeof(struct ospf_lsa_sum);
+ struct ospf_new_lsa lsa = {
+ .type = LSA_T_NET,
+ .dom = ifa->oa->areaid,
+ .id = ospf_is_v2(p) ? ipa_to_u32(ifa->addr->ip) : ifa->iface_id,
+ .opts = ifa->oa->options,
+ .ifa = ifa
+ };
- sum->netmask = ipa_mkmask(mlen);
- sum->metric = metric;
+ OSPF_TRACE(D_EVENTS, "Updating network state for %s (Id: %R)", ifa->ifname, lsa.id);
- return sum;
-}
-
-#define originate_sum_net_lsa_body(po,length,fn,metric) \
- originate_sum_lsa_body(po, length, (fn)->pxlen, metric)
-
-#define originate_sum_rt_lsa_body(po,length,drid,metric,options) \
- originate_sum_lsa_body(po, length, 0, metric)
-
-static inline int
-check_sum_net_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en)
-{
- struct ospf_lsa_sum *sum = en->lsa_body;
- return fn->pxlen != ipa_mklen(sum->netmask);
-}
+ if (ospf_is_v2(p))
+ prepare_net2_lsa_body(p, ifa);
+ else
+ prepare_net3_lsa_body(p, ifa);
-static inline int
-check_sum_lsa_same(struct top_hash_entry *en, u32 metric)
-{
- /* Netmask already checked in check_sum_net_lsaid_collision() */
- struct ospf_lsa_sum *sum = en->lsa_body;
- return (en->lsa.sn != LSA_MAXSEQNO) && (sum->metric == metric);
+ ifa->net_lsa = ospf_originate_lsa(p, &lsa);
}
-#define check_sum_net_lsa_same(en,metric) \
- check_sum_lsa_same(en, metric)
-
-#define check_sum_rt_lsa_same(en,drid,metric,options) \
- check_sum_lsa_same(en, metric)
+/*
+ * (Net|Rt)-summary-LSA handling
+ * (a.k.a. Inter-Area-(Prefix|Router)-LSA)
+ * Type = LSA_T_SUM_NET, LSA_T_SUM_RT
+ */
-#else /* OSPFv3 */
-
-static inline void *
-originate_sum_net_lsa_body(struct proto_ospf *po, u16 *length, struct fib_node *fn, u32 metric)
+static inline void
+prepare_sum2_lsa_body(struct ospf_proto *p, uint pxlen, u32 metric)
{
- int size = sizeof(struct ospf_lsa_sum_net) + IPV6_PREFIX_SPACE(fn->pxlen);
- struct ospf_lsa_sum_net *sum = mb_alloc(po->proto.pool, size);
- *length = sizeof(struct ospf_lsa_header) + size;
+ struct ospf_lsa_sum2 *sum;
+ sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum2));
+ sum->netmask = u32_mkmask(pxlen);
sum->metric = metric;
- put_ipv6_prefix(sum->prefix, fn->prefix, fn->pxlen, 0, 0);
-
- return sum;
}
-static inline int
-check_sum_net_lsaid_collision(struct fib_node *fn, struct top_hash_entry *en)
+static inline void
+prepare_sum3_net_lsa_body(struct ospf_proto *p, ort *nf, u32 metric)
{
- struct ospf_lsa_sum_net *sum = en->lsa_body;
- ip_addr prefix;
- int pxlen;
- u8 pxopts;
- u16 rest;
-
- lsa_get_ipv6_prefix(sum->prefix, &prefix, &pxlen, &pxopts, &rest);
- return (fn->pxlen != pxlen) || !ipa_equal(fn->prefix, prefix);
-}
+ struct ospf_lsa_sum3_net *sum;
-static inline int
-check_sum_net_lsa_same(struct top_hash_entry *en, u32 metric)
-{
- /* Prefix already checked in check_sum_net_lsaid_collision() */
- struct ospf_lsa_sum_net *sum = en->lsa_body;
- return (en->lsa.sn != LSA_MAXSEQNO) && (sum->metric == metric);
+ sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum3_net) + IPV6_PREFIX_SPACE(nf->fn.pxlen));
+ sum->metric = metric;
+ put_ipv6_prefix(sum->prefix, nf->fn.prefix, nf->fn.pxlen, 0, 0);
}
-static inline void *
-originate_sum_rt_lsa_body(struct proto_ospf *po, u16 *length, u32 drid, u32 metric, u32 options)
+static inline void
+prepare_sum3_rt_lsa_body(struct ospf_proto *p, u32 drid, u32 metric, u32 options)
{
- struct ospf_lsa_sum_rt *sum = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_sum_rt));
- *length = sizeof(struct ospf_lsa_header) + sizeof(struct ospf_lsa_sum_rt);
+ struct ospf_lsa_sum3_rt *sum;
+ sum = lsab_allocz(p, sizeof(struct ospf_lsa_sum3_rt));
sum->options = options;
sum->metric = metric;
sum->drid = drid;
-
- return sum;
}
-static inline int
-check_sum_rt_lsa_same(struct top_hash_entry *en, u32 drid, u32 metric, u32 options)
-{
- struct ospf_lsa_sum_rt *sum = en->lsa_body;
- return (en->lsa.sn != LSA_MAXSEQNO) && (sum->options == options) &&
- (sum->metric == metric) && (sum->drid == drid);
-}
-
-#endif
-
void
-originate_sum_net_lsa(struct ospf_area *oa, struct fib_node *fn, int metric)
-{
- struct proto_ospf *po = oa->po;
- struct proto *p = &po->proto;
- struct top_hash_entry *en;
- u32 dom = oa->areaid;
- struct ospf_lsa_header lsa;
- void *body;
-
- OSPF_TRACE(D_EVENTS, "Originating net-summary-LSA for %I/%d (metric %d)",
- fn->prefix, fn->pxlen, metric);
-
- /* options argument is used in ORT_NET and OSPFv3 only */
- lsa.age = 0;
-#ifdef OSPFv2
- lsa.options = oa->options;
-#endif
- lsa.type = LSA_T_SUM_NET;
- lsa.id = fibnode_to_lsaid(po, fn);
- lsa.rt = po->router_id;
-
- if ((en = ospf_hash_find_header(po->gr, dom, &lsa)) != NULL)
- {
- if (check_sum_net_lsaid_collision(fn, en))
- {
- log(L_ERR "%s: LSAID collision for %I/%d",
- p->name, fn->prefix, fn->pxlen);
- return;
- }
-
- if (check_sum_net_lsa_same(en, metric))
- return;
- }
- lsa.sn = get_seqnum(en);
-
- body = originate_sum_net_lsa_body(po, &lsa.length, fn, metric);
- lsasum_calculate(&lsa, body);
- lsa_install_new(po, &lsa, dom, body);
- ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
-}
-
-void
-originate_sum_rt_lsa(struct ospf_area *oa, struct fib_node *fn, int metric, u32 options UNUSED)
-{
- struct proto_ospf *po = oa->po;
- struct proto *p = &po->proto;
- struct top_hash_entry *en;
- u32 dom = oa->areaid;
- u32 rid = ipa_to_rid(fn->prefix);
- struct ospf_lsa_header lsa;
- void *body;
-
- OSPF_TRACE(D_EVENTS, "Originating rt-summary-LSA for %R (metric %d)",
- rid, metric);
-
- lsa.age = 0;
-#ifdef OSPFv2
- lsa.options = oa->options;
-#endif
- lsa.type = LSA_T_SUM_RT;
- /* In OSPFv3, LSA ID is meaningless, but we still use Router ID of ASBR */
- lsa.id = rid;
- lsa.rt = po->router_id;
-
- options &= OPTIONS_MASK;
- if ((en = ospf_hash_find_header(po->gr, dom, &lsa)) != NULL)
- {
- if (check_sum_rt_lsa_same(en, lsa.id, metric, options))
- return;
- }
- lsa.sn = get_seqnum(en);
+ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric)
+{
+ struct ospf_new_lsa lsa = {
+ .type = LSA_T_SUM_NET,
+ .mode = LSA_M_RTCALC,
+ .dom = oa->areaid,
+ .id = ort_to_lsaid(p, nf),
+ .opts = oa->options,
+ .nf = nf
+ };
+
+ if (ospf_is_v2(p))
+ prepare_sum2_lsa_body(p, nf->fn.pxlen, metric);
+ else
+ prepare_sum3_net_lsa_body(p, nf, metric);
- body = originate_sum_rt_lsa_body(po, &lsa.length, lsa.id, metric, options);
- lsasum_calculate(&lsa, body);
- lsa_install_new(po, &lsa, dom, body);
- ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
+ ospf_originate_lsa(p, &lsa);
}
void
-flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type)
-{
- struct proto_ospf *po = oa->po;
- struct proto *p = &po->proto;
- struct top_hash_entry *en;
- struct ospf_lsa_header lsa;
-
- lsa.rt = po->router_id;
- if (type == ORT_NET)
- {
- lsa.id = fibnode_to_lsaid(po, fn);
- lsa.type = LSA_T_SUM_NET;
- }
+ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric, u32 options)
+{
+ struct ospf_new_lsa lsa = {
+ .type = LSA_T_SUM_RT,
+ .mode = LSA_M_RTCALC,
+ .dom = oa->areaid,
+ .id = ipa_to_rid(nf->fn.prefix), /* Router ID of ASBR, irrelevant for OSPFv3 */
+ .opts = oa->options
+ };
+
+ if (ospf_is_v2(p))
+ prepare_sum2_lsa_body(p, 0, metric);
else
- {
- /* In OSPFv3, LSA ID is meaningless, but we still use Router ID of ASBR */
- lsa.id = ipa_to_rid(fn->prefix);
- lsa.type = LSA_T_SUM_RT;
- }
-
- if ((en = ospf_hash_find_header(po->gr, oa->areaid, &lsa)) != NULL)
- {
- OSPF_TRACE(D_EVENTS, "Flushing summary-LSA (id=%R, type=%d)",
- en->lsa.id, en->lsa.type);
-
- if ((type == ORT_NET) && check_sum_net_lsaid_collision(fn, en))
- {
- log(L_ERR "%s: LSAID collision for %I/%d",
- p->name, fn->prefix, fn->pxlen);
- return;
- }
+ prepare_sum3_rt_lsa_body(p, lsa.id, metric, options & LSA_OPTIONS_MASK);
- struct ospf_lsa_sum *sum = en->lsa_body;
- en->lsa.age = LSA_MAXAGE;
- en->lsa.sn = LSA_MAXSEQNO;
- lsasum_calculate(&en->lsa, sum);
- ospf_lsupd_flood(po, NULL, NULL, &en->lsa, oa->areaid, 1);
- if (can_flush_lsa(po)) flush_lsa(en, po);
- }
+ ospf_originate_lsa(p, &lsa);
}
-#ifdef OSPFv2
-
-static inline void *
-originate_ext_lsa_body(struct proto_ospf *po, u16 *length, struct fib_node *fn,
- u32 metric, ip_addr fwaddr, u32 tag, int pbit UNUSED)
-{
- struct ospf_lsa_ext *ext = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_ext));
- *length = sizeof(struct ospf_lsa_header) + sizeof(struct ospf_lsa_ext);
-
- ext->metric = metric;
- ext->netmask = ipa_mkmask(fn->pxlen);
- ext->fwaddr = fwaddr;
- ext->tag = tag;
-
- return ext;
-}
/*
- * check_ext_lsa() combines functions of check_*_lsaid_collision() and
- * check_*_lsa_same(). 'en' is existing ext LSA, and rest parameters
- * are parameters of new ext route. Function returns -1 if there is
- * LSAID collision, returns 1 if the existing LSA is the same and
- * returns 0 otherwise (in that case, we need to originate a new LSA).
- *
- * Really, checking for the same parameters is not as important as in
- * summary LSA origination, because in most cases the duplicate
- * external route propagation would be stopped by the nest. But there
- * are still some cases (route reload, the same route propagated through
- * different protocol) so it is also done here.
+ * AS-external-LSA and NSSA-LSA handling
+ * Type = LSA_T_EXT, LSA_T_NSSA
*/
-static inline int
-check_ext_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric, ip_addr fwaddr, u32 tag)
+static inline void
+prepare_ext2_lsa_body(struct ospf_proto *p, uint pxlen,
+ u32 metric, u32 ebit, ip_addr fwaddr, u32 tag)
{
- struct ospf_lsa_ext *ext = en->lsa_body;
+ struct ospf_lsa_ext2 *ext;
- /* LSAID collision */
- if (fn->pxlen != ipa_mklen(ext->netmask))
- return -1;
+ ext = lsab_allocz(p, sizeof(struct ospf_lsa_ext2));
+ ext->metric = metric & LSA_METRIC_MASK;
+ ext->netmask = u32_mkmask(pxlen);
+ ext->fwaddr = ipa_to_u32(fwaddr);
+ ext->tag = tag;
- return (en->lsa.sn != LSA_MAXSEQNO) && (ext->metric == metric) &&
- (ext->tag == tag) && ipa_equal(ext->fwaddr,fwaddr);
+ if (ebit)
+ ext->metric |= LSA_EXT2_EBIT;
}
-#else /* OSPFv3 */
-
-static inline void *
-originate_ext_lsa_body(struct proto_ospf *po, u16 *length, struct fib_node *fn,
- u32 metric, ip_addr fwaddr, u32 tag, int pbit)
+static inline void
+prepare_ext3_lsa_body(struct ospf_proto *p, ort *nf,
+ u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit)
{
- int size = sizeof(struct ospf_lsa_ext)
- + IPV6_PREFIX_SPACE(fn->pxlen)
+ struct ospf_lsa_ext3 *ext;
+ int bsize = sizeof(struct ospf_lsa_ext3)
+ + IPV6_PREFIX_SPACE(nf->fn.pxlen)
+ (ipa_nonzero(fwaddr) ? 16 : 0)
+ (tag ? 4 : 0);
- struct ospf_lsa_ext *ext = mb_alloc(po->proto.pool, size);
- *length = sizeof(struct ospf_lsa_header) + size;
+ ext = lsab_allocz(p, bsize);
+ ext->metric = metric & LSA_METRIC_MASK;
+ u32 *buf = ext->rest;
- ext->metric = metric;
+ buf = put_ipv6_prefix(buf, nf->fn.prefix, nf->fn.pxlen, pbit ? OPT_PX_P : 0, 0);
- u32 *buf = ext->rest;
- buf = put_ipv6_prefix(buf, fn->prefix, fn->pxlen, pbit ? OPT_PX_P : 0, 0);
+ if (ebit)
+ ext->metric |= LSA_EXT3_EBIT;
if (ipa_nonzero(fwaddr))
{
- ext->metric |= LSA_EXT_FBIT;
+ ext->metric |= LSA_EXT3_FBIT;
buf = put_ipv6_addr(buf, fwaddr);
}
if (tag)
{
- ext->metric |= LSA_EXT_TBIT;
+ ext->metric |= LSA_EXT3_TBIT;
*buf++ = tag;
}
-
- return ext;
}
-static inline int
-check_ext_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric, ip_addr fwaddr, u32 tag)
-{
- struct ospf_lsa_ext *ext = en->lsa_body;
- ip_addr prefix;
- int pxlen;
- u8 pxopts;
- u16 rest;
+/**
+ * originate_ext_lsa - new route received from nest and filters
+ * @p: OSPF protocol instance
+ * @oa: ospf_area for which LSA is originated
+ * @nf: network prefix and mask
+ * @mode: the mode of the LSA (LSA_M_EXPORT or LSA_M_RTCALC)
+ * @metric: the metric of a route
+ * @ebit: E-bit for route metric (bool)
+ * @fwaddr: the forwarding address
+ * @tag: the route tag
+ * @pbit: P-bit for NSSA LSAs (bool), ignored for external LSAs
+ *
+ * If I receive a message that new route is installed, I try to originate an
+ * external LSA. If @oa is an NSSA area, NSSA-LSA is originated instead.
+ * @oa should not be a stub area. @src does not specify whether the LSA
+ * is external or NSSA, but it specifies the source of origination -
+ * the export from ospf_rt_notify(), or the NSSA-EXT translation.
+ */
+void
+ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 mode,
+ u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit)
+{
+ struct ospf_new_lsa lsa = {
+ .type = oa ? LSA_T_NSSA : LSA_T_EXT,
+ .mode = mode, /* LSA_M_EXPORT or LSA_M_RTCALC */
+ .dom = oa ? oa->areaid : 0,
+ .id = ort_to_lsaid(p, nf),
+ .opts = oa ? (pbit ? OPT_P : 0) : OPT_E,
+ .nf = nf
+ };
+
+ if (ospf_is_v2(p))
+ prepare_ext2_lsa_body(p, nf->fn.pxlen, metric, ebit, fwaddr, tag);
+ else
+ prepare_ext3_lsa_body(p, nf, metric, ebit, fwaddr, tag, oa && pbit);
- u32 *buf = lsa_get_ipv6_prefix(ext->rest, &prefix, &pxlen, &pxopts, &rest);
+ ospf_originate_lsa(p, &lsa);
+}
- /* LSAID collision */
- if ((fn->pxlen != pxlen) || !ipa_equal(fn->prefix, prefix))
- return -1;
+static struct top_hash_entry *
+ospf_hash_find_(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type);
- if (en->lsa.sn == LSA_MAXSEQNO)
- return 0;
+static void
+ospf_flush_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf)
+{
+ struct top_hash_entry *en;
- u32 rt_metric = ext->metric & METRIC_MASK;
- ip_addr rt_fwaddr = IPA_NONE;
- u32 rt_tag = 0;
+ u32 type = oa ? LSA_T_NSSA : LSA_T_EXT;
+ u32 dom = oa ? oa->areaid : 0;
+ u32 id = ort_to_lsaid(p, nf);
- if (ext->metric & LSA_EXT_FBIT)
- buf = lsa_get_ipv6_addr(buf, &rt_fwaddr);
+ en = ospf_hash_find_(p->gr, dom, id, p->router_id, type);
- if (ext->metric & LSA_EXT_TBIT)
- rt_tag = *buf++;
+ if (!en || (en->nf != nf))
+ return;
- return (rt_metric == metric) && ipa_equal(rt_fwaddr, fwaddr) && (rt_tag == tag);
+ ospf_flush_lsa(p, en);
}
+static inline int
+use_gw_for_fwaddr(struct ospf_proto *p, ip_addr gw, struct iface *iface)
+{
+ struct ospf_iface *ifa;
-#endif
+ if (ipa_zero(gw) || ipa_is_link_local(gw))
+ return 0;
+
+ WALK_LIST(ifa, p->iface_list)
+ if ((ifa->iface == iface) &&
+ (!ospf_is_v2(p) || ipa_in_net(gw, ifa->addr->prefix, ifa->addr->pxlen)))
+ return 1;
+
+ return 0;
+}
static inline ip_addr
-find_surrogate_fwaddr(struct ospf_area *oa)
+find_surrogate_fwaddr(struct ospf_proto *p, struct ospf_area *oa)
{
- struct proto_ospf *po = oa->po;
struct ospf_iface *ifa;
struct ifa *a, *cur_addr = NULL;
int np, cur_np = 0;
- WALK_LIST(ifa, po->iface_list)
+ /* RFC 3101 2.3 - surrogate forwarding address selection */
+
+ WALK_LIST(ifa, p->iface_list)
{
if ((ifa->oa != oa) ||
(ifa->type == OSPF_IT_VLINK))
continue;
-#ifdef OSPFv2
- a = ifa->addr;
- if (a->flags & IA_PEER)
- continue;
-
- np = ((a->flags & IA_HOST) || ifa->stub) ? 2 : 1;
- if (np > cur_np)
+ if (ospf_is_v2(p))
{
- cur_addr = a;
- cur_np = np;
- }
-
-#else /* OSPFv3 */
- WALK_LIST(a, ifa->iface->addrs)
- {
- if ((a->flags & IA_SECONDARY) ||
- (a->flags & IA_PEER) ||
- (a->scope <= SCOPE_LINK))
+ a = ifa->addr;
+ if (a->flags & IA_PEER)
continue;
- np = ((a->flags & IA_HOST) || ifa->stub) ? 2 : 1;
+ np = (a->flags & IA_HOST) ? 3 : (ifa->stub ? 2 : 1);
if (np > cur_np)
{
cur_addr = a;
cur_np = np;
}
}
-#endif
+ else /* OSPFv3 */
+ {
+ WALK_LIST(a, ifa->iface->addrs)
+ {
+ if ((a->flags & IA_SECONDARY) ||
+ (a->flags & IA_PEER) ||
+ (a->scope <= SCOPE_LINK))
+ continue;
+
+ np = (a->flags & IA_HOST) ? 3 : (ifa->stub ? 2 : 1);
+ if (np > cur_np)
+ {
+ cur_addr = a;
+ cur_np = np;
+ }
+ }
+ }
}
return cur_addr ? cur_addr->ip : IPA_NONE;
}
-
-/**
- * originate_ext_lsa - new route received from nest and filters
- * @oa: ospf_area for which LSA is originated
- * @fn: network prefix and mask
- * @src: the source of origination of the LSA (EXT_EXPORT/EXT_NSSA)
- * @metric: the metric of a route
- * @fwaddr: the forwarding address
- * @tag: the route tag
- * @pbit: P-bit for NSSA LSAs, ignored for external LSAs
- *
- * If I receive a message that new route is installed, I try to originate an
- * external LSA. If @oa is an NSSA area, NSSA-LSA is originated instead.
- * @oa should not be a stub area. @src does not specify whether the LSA
- * is external or NSSA, but it specifies the source of origination -
- * the export from ospf_rt_notify(), or the NSSA-EXT translation.
- *
- * The function also sets flag ebit. If it's the first time, the new router lsa
- * origination is necessary.
- */
void
-originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int src,
- u32 metric, ip_addr fwaddr, u32 tag, int pbit)
-{
- struct proto_ospf *po = oa->po;
- struct proto *p = &po->proto;
- struct ospf_lsa_header lsa;
- struct top_hash_entry *en = NULL;
- void *body;
- int nssa = oa_is_nssa(oa);
- u32 dom = nssa ? oa->areaid : 0;
-
- OSPF_TRACE(D_EVENTS, "Originating %s-LSA for %I/%d",
- nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen);
-
- lsa.age = 0;
-#ifdef OSPFv2
- lsa.options = nssa ? (pbit ? OPT_P : 0) : OPT_E;
-#endif
- lsa.type = nssa ? LSA_T_NSSA : LSA_T_EXT;
- lsa.id = fibnode_to_lsaid(po, fn);
- lsa.rt = po->router_id;
-
- if (nssa && pbit && ipa_zero(fwaddr))
- {
- /* NSSA-LSA with P-bit set must have non-zero forwarding address */
+ospf_rt_notify(struct proto *P, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *ea)
+{
+ struct ospf_proto *p = (struct ospf_proto *) P;
+ struct ospf_area *oa = NULL; /* non-NULL for NSSA-LSA */
+ ort *nf;
- fwaddr = find_surrogate_fwaddr(oa);
- if (ipa_zero(fwaddr))
- {
- log(L_ERR "%s: Cannot find forwarding address for NSSA-LSA %I/%d",
- p->name, fn->prefix, fn->pxlen);
- return;
- }
- }
+ /*
+ * There are several posibilities:
+ * 1) router in regular area - originate external LSA with global scope
+ * 2) router in NSSA area - originate area-specific NSSA-LSA
+ * 3) router in stub area - cannot export routes
+ * 4) area border router - same as (1), it is attached to backbone
+ */
- if ((en = ospf_hash_find_header(po->gr, dom, &lsa)) != NULL)
+ if ((p->areano == 1) && oa_is_nssa(HEAD(p->area_list)))
+ oa = HEAD(p->area_list);
+
+ if (!new)
{
- int rv = check_ext_lsa(en, fn, metric, fwaddr, tag);
- if (rv < 0)
- {
- log(L_ERR "%s: LSAID collision for %I/%d",
- p->name, fn->prefix, fn->pxlen);
- return;
- }
+ nf = (ort *) fib_find(&p->rtf, &n->n.prefix, n->n.pxlen);
- if (rv > 0)
+ if (!nf || !nf->external_rte)
return;
+
+ ospf_flush_ext_lsa(p, oa, nf);
+ nf->external_rte = 0;
+
+ /* Old external route might blocked some NSSA translation */
+ if ((p->areano > 1) && rt_is_nssa(nf) && nf->n.oa->translate)
+ ospf_schedule_rtcalc(p);
+
+ return;
}
- lsa.sn = get_seqnum(en);
- body = originate_ext_lsa_body(po, &lsa.length, fn, metric, fwaddr, tag, pbit);
- lsasum_calculate(&lsa, body);
+ ASSERT(p->asbr);
+
+ /* Get route attributes */
+ rta *a = new->attrs;
+ u32 m1 = ea_get_int(ea, EA_OSPF_METRIC1, LSINFINITY);
+ u32 m2 = ea_get_int(ea, EA_OSPF_METRIC2, 10000);
+ int ebit = (m1 == LSINFINITY);
+ u32 metric = ebit ? m2 : m1;
+ u32 tag = ea_get_int(ea, EA_OSPF_TAG, 0);
+ ip_addr fwd = IPA_NONE;
- if (src)
- fn->x1 = src;
- lsa_install_new(po, &lsa, dom, body);
- ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
+ if ((a->dest == RTD_ROUTER) && use_gw_for_fwaddr(p, a->gw, a->iface))
+ fwd = a->gw;
- if (po->ebit == 0)
+ /* NSSA-LSA with P-bit set must have non-zero forwarding address */
+ if (oa && ipa_zero(fwd))
{
- po->ebit = 1;
- WALK_LIST(oa, po->area_list)
+ fwd = find_surrogate_fwaddr(p, oa);
+
+ if (ipa_zero(fwd))
{
- schedule_rt_lsa(oa);
+ log(L_ERR "%s: Cannot find forwarding address for NSSA-LSA %I/%d",
+ p->p.name, n->n.prefix, n->n.pxlen);
+ return;
}
}
-}
-
-void
-flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int nssa)
-{
- struct proto_ospf *po = oa->po;
- struct proto *p = &po->proto;
- struct top_hash_entry *en;
- u32 dom = nssa ? oa->areaid : 0;
- u32 type = nssa ? LSA_T_NSSA : LSA_T_EXT;
- u32 lsaid = fibnode_to_lsaid(po, fn);
+ nf = (ort *) fib_get(&p->rtf, &n->n.prefix, n->n.pxlen);
+ ospf_originate_ext_lsa(p, oa, nf, LSA_M_EXPORT, metric, ebit, fwd, tag, 1);
+ nf->external_rte = 1;
+}
- if (en = ospf_hash_find(po->gr, dom, lsaid, po->router_id, type))
- {
- OSPF_TRACE(D_EVENTS, "Flushing %s-LSA for %I/%d",
- nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen);
- if (check_ext_lsa(en, fn, 0, IPA_NONE, 0) < 0)
- {
- log(L_ERR "%s: LSAID collision for %I/%d",
- p->name, fn->prefix, fn->pxlen);
- return;
- }
+/*
+ * Link-LSA handling (assume OSPFv3)
+ * Type = LSA_T_LINK
+ */
- fn->x1 = 0;
- ospf_lsupd_flush_nlsa(po, en);
- }
+static inline void
+lsab_put_prefix(struct ospf_proto *p, ip_addr prefix, u32 pxlen, u32 cost)
+{
+ void *buf = lsab_alloc(p, IPV6_PREFIX_SPACE(pxlen));
+ u8 flags = (pxlen < MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA;
+ put_ipv6_prefix(buf, prefix, pxlen, flags, cost);
}
-
-#ifdef OSPFv3
-
-static void *
-originate_link_lsa_body(struct ospf_iface *ifa, u16 *length)
+static void
+prepare_link_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
{
- struct proto_ospf *po = ifa->oa->po;
struct ospf_lsa_link *ll;
int i = 0;
- u8 flags;
- ASSERT(po->lsab_used == 0);
- ll = lsab_allocz(po, sizeof(struct ospf_lsa_link));
+ ASSERT(p->lsab_used == 0);
+ ll = lsab_allocz(p, sizeof(struct ospf_lsa_link));
ll->options = ifa->oa->options | (ifa->priority << 24);
- ll->lladdr = ifa->addr->ip;
+ ll->lladdr = ipa_to_ip6(ifa->addr->ip);
ll = NULL; /* buffer might be reallocated later */
struct ifa *a;
WALK_LIST(a, ifa->iface->addrs)
- {
- if ((a->flags & IA_SECONDARY) ||
- (a->scope < SCOPE_SITE))
- continue;
+ {
+ if ((a->flags & IA_SECONDARY) ||
+ (a->scope < SCOPE_SITE))
+ continue;
- flags = (a->pxlen < MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA;
- put_ipv6_prefix(lsab_alloc(po, IPV6_PREFIX_SPACE(a->pxlen)),
- a->ip, a->pxlen, flags, 0);
- i++;
- }
+ lsab_put_prefix(p, a->prefix, a->pxlen, 0);
+ i++;
+ }
- ll = po->lsab;
+ ll = p->lsab;
ll->pxcount = i;
- *length = po->lsab_used + sizeof(struct ospf_lsa_header);
- return lsab_flush(po);
}
-void
-originate_link_lsa(struct ospf_iface *ifa)
+static void
+ospf_originate_link_lsa(struct ospf_proto *p, struct ospf_iface *ifa)
{
- struct ospf_lsa_header lsa;
- struct proto_ospf *po = ifa->oa->po;
- struct proto *p = &po->proto;
- void *body;
-
- /* Vlinks do not have link-LSAs */
- if (ifa->type == OSPF_IT_VLINK)
+ if (ospf_is_v2(p))
return;
- OSPF_TRACE(D_EVENTS, "Originating link-LSA for iface %s", ifa->ifname);
+ struct ospf_new_lsa lsa = {
+ .type = LSA_T_LINK,
+ .dom = ifa->iface_id,
+ .id = ifa->iface_id,
+ .ifa = ifa
+ };
- lsa.age = 0;
- lsa.type = LSA_T_LINK;
- lsa.id = ifa->iface_id;
- lsa.rt = po->router_id;
- lsa.sn = get_seqnum(ifa->link_lsa);
- u32 dom = ifa->iface_id;
+ OSPF_TRACE(D_EVENTS, "Updating link state for %s (Id: %R)", ifa->ifname, lsa.id);
- body = originate_link_lsa_body(ifa, &lsa.length);
- lsasum_calculate(&lsa, body);
- ifa->link_lsa = lsa_install_new(po, &lsa, dom, body);
- ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
+ prepare_link_lsa_body(p, ifa);
- /* Just to be sure to not forget on our link LSA */
- if (ifa->state == OSPF_IS_DR)
- schedule_net_lsa(ifa);
+ ifa->link_lsa = ospf_originate_lsa(p, &lsa);
}
-void
-update_link_lsa(struct ospf_iface *ifa)
-{
- if (ifa->link_lsa && ((ifa->link_lsa->inst_t + MINLSINTERVAL) > now))
- return;
- /*
- * It's too early to originate new link LSA. We will
- * try to do it next tick
- */
- originate_link_lsa(ifa);
- ifa->origlink = 0;
-}
-static inline void
-lsa_put_prefix(struct proto_ospf *po, ip_addr prefix, u32 pxlen, u32 cost)
-{
- put_ipv6_prefix(lsab_alloc(po, IPV6_PREFIX_SPACE(pxlen)), prefix, pxlen,
- (pxlen < MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA, cost);
-}
+/*
+ * Prefix-Rt-LSA handling (assume OSPFv3)
+ * Type = LSA_T_PREFIX, referred type = LSA_T_RT
+ */
-static void *
-originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length)
+static void
+prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
{
- struct proto_ospf *po = oa->po;
- struct ospf_config *cf = (struct ospf_config *) (po->proto.cf);
+ struct ospf_config *cf = (struct ospf_config *) (p->p.cf);
struct ospf_iface *ifa;
struct ospf_lsa_prefix *lp;
int host_addr = 0;
int net_lsa;
int i = 0;
- ASSERT(po->lsab_used == 0);
- lp = lsab_allocz(po, sizeof(struct ospf_lsa_prefix));
+ ASSERT(p->lsab_used == 0);
+ lp = lsab_allocz(p, sizeof(struct ospf_lsa_prefix));
lp->ref_type = LSA_T_RT;
lp->ref_id = 0;
- lp->ref_rt = po->router_id;
+ lp->ref_rt = p->router_id;
lp = NULL; /* buffer might be reallocated later */
- WALK_LIST(ifa, po->iface_list)
+ WALK_LIST(ifa, p->iface_list)
{
if ((ifa->oa != oa) || (ifa->type == OSPF_IT_VLINK) || (ifa->state == OSPF_IS_DOWN))
continue;
@@ -1286,47 +1400,46 @@ originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length)
struct ifa *a;
WALK_LIST(a, ifa->iface->addrs)
- {
- if ((a->flags & IA_SECONDARY) ||
- (a->flags & IA_PEER) ||
- (a->scope <= SCOPE_LINK))
- continue;
+ {
+ if ((a->flags & IA_SECONDARY) ||
+ (a->flags & IA_PEER) ||
+ (a->scope <= SCOPE_LINK))
+ continue;
- if (((a->pxlen < MAX_PREFIX_LENGTH) && net_lsa) ||
- configured_stubnet(oa, a))
- continue;
+ if (((a->pxlen < MAX_PREFIX_LENGTH) && net_lsa) ||
+ configured_stubnet(oa, a))
+ continue;
- if ((a->flags & IA_HOST) ||
- (ifa->state == OSPF_IS_LOOP) ||
- (ifa->type == OSPF_IT_PTMP))
- {
- lsa_put_prefix(po, a->ip, MAX_PREFIX_LENGTH, 0);
- host_addr = 1;
- }
- else
- lsa_put_prefix(po, a->prefix, a->pxlen, ifa->cost);
- i++;
+ if ((a->flags & IA_HOST) ||
+ (ifa->state == OSPF_IS_LOOP) ||
+ (ifa->type == OSPF_IT_PTMP))
+ {
+ lsab_put_prefix(p, a->ip, MAX_PREFIX_LENGTH, 0);
+ host_addr = 1;
}
+ else
+ lsab_put_prefix(p, a->prefix, a->pxlen, ifa->cost);
+ i++;
+ }
ifa->px_pos_end = i;
}
struct ospf_stubnet_config *sn;
- if (oa->ac)
- WALK_LIST(sn, oa->ac->stubnet_list)
- if (!sn->hidden)
- {
- lsa_put_prefix(po, sn->px.addr, sn->px.len, sn->cost);
- if (sn->px.len == MAX_PREFIX_LENGTH)
- host_addr = 1;
- i++;
- }
+ WALK_LIST(sn, oa->ac->stubnet_list)
+ if (!sn->hidden)
+ {
+ lsab_put_prefix(p, sn->px.addr, sn->px.len, sn->cost);
+ if (sn->px.len == MAX_PREFIX_LENGTH)
+ host_addr = 1;
+ i++;
+ }
/* If there are some configured vlinks, find some global address
(even from another area), which will be used as a vlink endpoint. */
if (!EMPTY_LIST(cf->vlink_list) && !host_addr)
{
- WALK_LIST(ifa, po->iface_list)
+ WALK_LIST(ifa, p->iface_list)
{
if ((ifa->type == OSPF_IT_VLINK) || (ifa->state == OSPF_IS_DOWN))
continue;
@@ -1338,7 +1451,7 @@ originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length)
continue;
/* Found some IP */
- lsa_put_prefix(po, a->ip, MAX_PREFIX_LENGTH, 0);
+ lsab_put_prefix(p, a->ip, MAX_PREFIX_LENGTH, 0);
i++;
goto done;
}
@@ -1346,36 +1459,33 @@ originate_prefix_rt_lsa_body(struct ospf_area *oa, u16 *length)
}
done:
- lp = po->lsab;
+ lp = p->lsab;
lp->pxcount = i;
- *length = po->lsab_used + sizeof(struct ospf_lsa_header);
- return lsab_flush(po);
}
-void
-originate_prefix_rt_lsa(struct ospf_area *oa)
+static void
+ospf_originate_prefix_rt_lsa(struct ospf_proto *p, struct ospf_area *oa)
{
- struct proto_ospf *po = oa->po;
- struct proto *p = &po->proto;
- struct ospf_lsa_header lsa;
- void *body;
+ if (ospf_is_v2(p))
+ return;
- OSPF_TRACE(D_EVENTS, "Originating router prefix-LSA for area %R", oa->areaid);
+ struct ospf_new_lsa lsa = {
+ .type = LSA_T_PREFIX,
+ .dom = oa->areaid,
+ .id = 0
+ };
- lsa.age = 0;
- lsa.type = LSA_T_PREFIX;
- lsa.id = 0;
- lsa.rt = po->router_id;
- lsa.sn = get_seqnum(oa->pxr_lsa);
- u32 dom = oa->areaid;
+ prepare_prefix_rt_lsa_body(p, oa);
- body = originate_prefix_rt_lsa_body(oa, &lsa.length);
- lsasum_calculate(&lsa, body);
- oa->pxr_lsa = lsa_install_new(po, &lsa, dom, body);
- ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
+ ospf_originate_lsa(p, &lsa);
}
+/*
+ * Prefix-Net-LSA handling (assume OSPFv3)
+ * Type = LSA_T_PREFIX, referred type = LSA_T_NET
+ */
+
static inline int
prefix_space(u32 *buf)
{
@@ -1389,7 +1499,7 @@ prefix_same(u32 *b1, u32 *b2)
int pxl1 = *b1 >> 24;
int pxl2 = *b2 >> 24;
int pxs, i;
-
+
if (pxl1 != pxl2)
return 0;
@@ -1410,9 +1520,9 @@ prefix_advance(u32 *buf)
/* FIXME eliminate items with LA bit set? see 4.4.3.9 */
static void
-add_prefix(struct proto_ospf *po, u32 *px, int offset, int *pxc)
+add_prefix(struct ospf_proto *p, u32 *px, int offset, int *pxc)
{
- u32 *pxl = lsab_offset(po, offset);
+ u32 *pxl = lsab_offset(p, offset);
int i;
for (i = 0; i < *pxc; pxl = prefix_advance(pxl), i++)
if (prefix_same(px, pxl))
@@ -1422,19 +1532,18 @@ add_prefix(struct proto_ospf *po, u32 *px, int offset, int *pxc)
return;
}
- ASSERT(pxl == lsab_end(po));
+ ASSERT(pxl == lsab_end(p));
int pxspace = prefix_space(px);
- pxl = lsab_alloc(po, pxspace);
+ pxl = lsab_alloc(p, pxspace);
memcpy(pxl, px, pxspace);
*pxl &= 0xFFFF0000; /* Set metric to zero */
(*pxc)++;
}
static void
-add_link_lsa(struct proto_ospf *po, struct top_hash_entry *en, int offset, int *pxc)
+add_link_lsa(struct ospf_proto *p, struct ospf_lsa_link *ll, int offset, int *pxc)
{
- struct ospf_lsa_link *ll = en->lsa_body;
u32 *pxb = ll->rest;
int j;
@@ -1451,92 +1560,129 @@ add_link_lsa(struct proto_ospf *po, struct top_hash_entry *en, int offset, int *
if ((pxlen >= 10) && ((pxb[1] & 0xffc00000) == 0xfe800000))
continue;
- add_prefix(po, pxb, offset, pxc);
+ add_prefix(p, pxb, offset, pxc);
}
}
-
-
-static void *
-originate_prefix_net_lsa_body(struct ospf_iface *ifa, u16 *length)
+static void
+prepare_prefix_net_lsa_body(struct ospf_proto *p, struct ospf_iface *ifa)
{
- struct proto_ospf *po = ifa->oa->po;
struct ospf_lsa_prefix *lp;
struct ospf_neighbor *n;
struct top_hash_entry *en;
int pxc, offset;
- ASSERT(po->lsab_used == 0);
- lp = lsab_allocz(po, sizeof(struct ospf_lsa_prefix));
+ ASSERT(p->lsab_used == 0);
+ lp = lsab_allocz(p, sizeof(struct ospf_lsa_prefix));
lp->ref_type = LSA_T_NET;
lp->ref_id = ifa->net_lsa->lsa.id;
- lp->ref_rt = po->router_id;
+ lp->ref_rt = p->router_id;
lp = NULL; /* buffer might be reallocated later */
pxc = 0;
- offset = po->lsab_used;
+ offset = p->lsab_used;
/* Find all Link LSAs associated with the link and merge their prefixes */
- if (ifa->link_lsa)
- add_link_lsa(po, ifa->link_lsa, offset, &pxc);
+ if (en = ifa->link_lsa)
+ add_link_lsa(p, en->next_lsa_body ?: en->lsa_body, offset, &pxc);
WALK_LIST(n, ifa->neigh_list)
if ((n->state == NEIGHBOR_FULL) &&
- (en = ospf_hash_find(po->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK)))
- add_link_lsa(po, en, offset, &pxc);
+ (en = ospf_hash_find(p->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK)))
+ add_link_lsa(p, en->lsa_body, offset, &pxc);
- lp = po->lsab;
+ lp = p->lsab;
lp->pxcount = pxc;
- *length = po->lsab_used + sizeof(struct ospf_lsa_header);
- return lsab_flush(po);
}
-void
-originate_prefix_net_lsa(struct ospf_iface *ifa)
+static void
+ospf_originate_prefix_net_lsa(struct ospf_proto *p, struct ospf_iface *ifa)
{
- struct proto_ospf *po = ifa->oa->po;
- struct proto *p = &po->proto;
- struct ospf_lsa_header lsa;
- void *body;
+ if (ospf_is_v2(p))
+ return;
- OSPF_TRACE(D_EVENTS, "Originating network prefix-LSA for iface %s", ifa->ifname);
+ struct ospf_new_lsa lsa = {
+ .type = LSA_T_PREFIX,
+ .dom = ifa->oa->areaid,
+ .id = ifa->iface_id,
+ .ifa = ifa
+ };
- lsa.age = 0;
- lsa.type = LSA_T_PREFIX;
- lsa.id = ifa->iface_id;
- lsa.rt = po->router_id;
- lsa.sn = get_seqnum(ifa->pxn_lsa);
- u32 dom = ifa->oa->areaid;
+ prepare_prefix_net_lsa_body(p, ifa);
- body = originate_prefix_net_lsa_body(ifa, &lsa.length);
- lsasum_calculate(&lsa, body);
- ifa->pxn_lsa = lsa_install_new(po, &lsa, dom, body);
- ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
+ ifa->pxn_lsa = ospf_originate_lsa(p, &lsa);
}
+static inline int breaks_minlsinterval(struct top_hash_entry *en)
+{ return en && (en->lsa.age < LSA_MAXAGE) && ((en->inst_time + MINLSINTERVAL) > now); }
+
void
-flush_prefix_net_lsa(struct ospf_iface *ifa)
+ospf_update_topology(struct ospf_proto *p)
{
- struct proto_ospf *po = ifa->oa->po;
- struct proto *p = &po->proto;
- struct top_hash_entry *en = ifa->pxn_lsa;
- u32 dom = ifa->oa->areaid;
+ struct ospf_area *oa;
+ struct ospf_iface *ifa;
- if (en == NULL)
- return;
+ WALK_LIST(oa, p->area_list)
+ {
+ if (oa->update_rt_lsa)
+ {
+ /*
+ * Generally, MinLSInterval is enforced in ospf_do_originate_lsa(), but
+ * origination of (prefix) router LSA is a special case. We do not want to
+ * prepare a new router LSA body and then postpone it in en->next_lsa_body
+ * for later origination, because there are side effects (updates of
+ * rt_pos_* and px_pos_* in ospf_iface structures) during that, which may
+ * confuse routing table calculation if executed after LSA body
+ * preparation but before final LSA origination (as rtcalc would use
+ * current rt_pos_* indexes but the old router LSA body).
+ *
+ * Here, we ensure that MinLSInterval is observed and we do not even try
+ * to originate these LSAs if it is not. Therefore, origination, when
+ * requested, will succeed unless there is also a seqnum wrapping, which
+ * is not a problem because in that case rtcalc is blocked by MaxAge.
+ */
+
+ if (breaks_minlsinterval(oa->rt) || breaks_minlsinterval(oa->pxr_lsa))
+ continue;
- OSPF_TRACE(D_EVENTS, "Flushing network prefix-LSA for iface %s", ifa->ifname);
+ ospf_originate_rt_lsa(p, oa);
+ ospf_originate_prefix_rt_lsa(p, oa);
+ oa->update_rt_lsa = 0;
+ }
+ }
- en->lsa.sn += 1;
- en->lsa.age = LSA_MAXAGE;
- lsasum_calculate(&en->lsa, en->lsa_body);
- ospf_lsupd_flood(po, NULL, NULL, &en->lsa, dom, 0);
- flush_lsa(en, po);
- ifa->pxn_lsa = NULL;
-}
+ WALK_LIST(ifa, p->iface_list)
+ {
+ if (ifa->type == OSPF_IT_VLINK)
+ continue;
+ if (ifa->update_link_lsa)
+ {
+ if ((ifa->state > OSPF_IS_LOOP) && !ifa->link_lsa_suppression)
+ ospf_originate_link_lsa(p, ifa);
+ else
+ ospf_flush2_lsa(p, &ifa->link_lsa);
-#endif
+ ifa->update_link_lsa = 0;
+ }
+
+ if (ifa->update_net_lsa)
+ {
+ if ((ifa->state == OSPF_IS_DR) && (ifa->fadj > 0))
+ {
+ ospf_originate_net_lsa(p, ifa);
+ ospf_originate_prefix_net_lsa(p, ifa);
+ }
+ else
+ {
+ ospf_flush2_lsa(p, &ifa->net_lsa);
+ ospf_flush2_lsa(p, &ifa->pxn_lsa);
+ }
+
+ ifa->update_net_lsa = 0;
+ }
+ }
+}
static void
@@ -1574,7 +1720,7 @@ ospf_top_hash_u32(u32 a)
return a;
}
-static inline unsigned
+static uint
ospf_top_hash(struct top_graph *f, u32 domain, u32 lsaid, u32 rtrid, u32 type)
{
/* In OSPFv2, we don't know Router ID when looking for network LSAs.
@@ -1582,14 +1728,8 @@ ospf_top_hash(struct top_graph *f, u32 domain, u32 lsaid, u32 rtrid, u32 type)
In both cases, there is (usually) just one (or small number)
appropriate LSA, so we just clear unknown part of key. */
- return (
-#ifdef OSPFv2
- ((type == LSA_T_NET) ? 0 : ospf_top_hash_u32(rtrid)) +
- ospf_top_hash_u32(lsaid) +
-#else /* OSPFv3 */
- ospf_top_hash_u32(rtrid) +
- ((type == LSA_T_RT) ? 0 : ospf_top_hash_u32(lsaid)) +
-#endif
+ return (((f->ospf2 && (type == LSA_T_NET)) ? 0 : ospf_top_hash_u32(rtrid)) +
+ ((!f->ospf2 && (type == LSA_T_RT)) ? 0 : ospf_top_hash_u32(lsaid)) +
type + domain) & f->hash_mask;
/*
@@ -1600,13 +1740,15 @@ ospf_top_hash(struct top_graph *f, u32 domain, u32 lsaid, u32 rtrid, u32 type)
/**
* ospf_top_new - allocated new topology database
- * @p: current instance of ospf
+ * @p: OSPF protocol instance
+ * @pool: pool for allocation
*
- * this dynamically hashed structure is often used for keeping lsas. mainly
- * its used in @ospf_area structure.
+ * This dynamically hashed structure is used for keeping LSAs. Mainly it is used
+ * for the LSA database of the OSPF protocol, but also for LSA retransmission
+ * and request lists of OSPF neighbors.
*/
struct top_graph *
-ospf_top_new(pool *pool)
+ospf_top_new(struct ospf_proto *p, pool *pool)
{
struct top_graph *f;
@@ -1617,6 +1759,7 @@ ospf_top_new(pool *pool)
ospf_top_ht_alloc(f);
f->hash_entries = 0;
f->hash_entries_min = 0;
+ f->ospf2 = ospf_is_v2(p);
return f;
}
@@ -1631,8 +1774,8 @@ ospf_top_free(struct top_graph *f)
static void
ospf_top_rehash(struct top_graph *f, int step)
{
- unsigned int oldn, oldh;
struct top_hash_entry **n, **oldt, **newt, *e, *x;
+ uint oldn, oldh;
oldn = f->hash_size;
oldt = f->hash_table;
@@ -1648,7 +1791,7 @@ ospf_top_rehash(struct top_graph *f, int step)
while (e)
{
x = e->next;
- n = newt + ospf_top_hash(f, e->domain, e->lsa.id, e->lsa.rt, e->lsa.type);
+ n = newt + ospf_top_hash(f, e->domain, e->lsa.id, e->lsa.rt, e->lsa_type);
e->next = *n;
*n = e;
e = x;
@@ -1657,124 +1800,94 @@ ospf_top_rehash(struct top_graph *f, int step)
ospf_top_ht_free(oldt);
}
-#ifdef OSPFv2
-
-u32
-ospf_lsa_domain(u32 type, struct ospf_iface *ifa)
-{
- return (type == LSA_T_EXT) ? 0 : ifa->oa->areaid;
-}
-
-#else /* OSPFv3 */
-
-u32
-ospf_lsa_domain(u32 type, struct ospf_iface *ifa)
-{
- switch (type & LSA_SCOPE_MASK)
- {
- case LSA_SCOPE_LINK:
- return ifa->iface_id;
-
- case LSA_SCOPE_AREA:
- return ifa->oa->areaid;
-
- case LSA_SCOPE_AS:
- default:
- return 0;
- }
-}
-
-#endif
-
-struct top_hash_entry *
-ospf_hash_find_header(struct top_graph *f, u32 domain, struct ospf_lsa_header *h)
-{
- return ospf_hash_find(f, domain, h->id, h->rt, h->type);
-}
-
-struct top_hash_entry *
-ospf_hash_get_header(struct top_graph *f, u32 domain, struct ospf_lsa_header *h)
-{
- return ospf_hash_get(f, domain, h->id, h->rt, h->type);
-}
-
-struct top_hash_entry *
-ospf_hash_find(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type)
+static struct top_hash_entry *
+ospf_hash_find_(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type)
{
struct top_hash_entry *e;
e = f->hash_table[ospf_top_hash(f, domain, lsa, rtr, type)];
- while (e && (e->lsa.id != lsa || e->lsa.type != type || e->lsa.rt != rtr || e->domain != domain))
+ while (e && (e->lsa.id != lsa || e->lsa.rt != rtr ||
+ e->lsa_type != type || e->domain != domain))
e = e->next;
return e;
}
-
-#ifdef OSPFv2
-
-/* In OSPFv2, sometimes we don't know Router ID when looking for network LSAs.
- There should be just one, so we find any match. */
struct top_hash_entry *
-ospf_hash_find_net(struct top_graph *f, u32 domain, u32 lsa)
+ospf_hash_find(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type)
{
- struct top_hash_entry *e;
- e = f->hash_table[ospf_top_hash(f, domain, lsa, 0, LSA_T_NET)];
-
- while (e && (e->lsa.id != lsa || e->lsa.type != LSA_T_NET || e->domain != domain))
- e = e->next;
+ struct top_hash_entry *e = ospf_hash_find_(f, domain, lsa, rtr, type);
- return e;
+ /* Hide hash entry with empty lsa_body */
+ return (e && e->lsa_body) ? e : NULL;
}
-#endif
-
-
-#ifdef OSPFv3
-
-/* In OSPFv3, usually we don't know LSA ID when looking for router
- LSAs. We return matching LSA with smallest LSA ID. */
+/* In OSPFv2, lsa.id is the same as lsa.rt for router LSA. In OSPFv3, we don't know
+ lsa.id when looking for router LSAs. We return matching LSA with smallest lsa.id. */
struct top_hash_entry *
ospf_hash_find_rt(struct top_graph *f, u32 domain, u32 rtr)
{
struct top_hash_entry *rv = NULL;
struct top_hash_entry *e;
- e = f->hash_table[ospf_top_hash(f, domain, 0, rtr, LSA_T_RT)];
-
+ /* We can put rtr for lsa.id to hash fn, it is ignored in OSPFv3 */
+ e = f->hash_table[ospf_top_hash(f, domain, rtr, rtr, LSA_T_RT)];
+
while (e)
+ {
+ if (e->lsa.rt == rtr && e->lsa_type == LSA_T_RT && e->domain == domain && e->lsa_body)
{
- if (e->lsa.rt == rtr && e->lsa.type == LSA_T_RT && e->domain == domain)
- if (!rv || e->lsa.id < rv->lsa.id)
- rv = e;
- e = e->next;
+ if (f->ospf2 && (e->lsa.id == rtr))
+ return e;
+ if (!f->ospf2 && (!rv || e->lsa.id < rv->lsa.id))
+ rv = e;
}
+ e = e->next;
+ }
return rv;
}
+/*
+ * ospf_hash_find_rt3_first() and ospf_hash_find_rt3_next() are used exclusively
+ * for lsa_walk_rt_init(), lsa_walk_rt(), therefore they skip MaxAge entries.
+ */
static inline struct top_hash_entry *
-find_matching_rt(struct top_hash_entry *e, u32 domain, u32 rtr)
+find_matching_rt3(struct top_hash_entry *e, u32 domain, u32 rtr)
{
- while (e && (e->lsa.rt != rtr || e->lsa.type != LSA_T_RT || e->domain != domain))
+ while (e && (e->lsa.rt != rtr || e->lsa_type != LSA_T_RT ||
+ e->domain != domain || e->lsa.age == LSA_MAXAGE))
e = e->next;
return e;
}
struct top_hash_entry *
-ospf_hash_find_rt_first(struct top_graph *f, u32 domain, u32 rtr)
+ospf_hash_find_rt3_first(struct top_graph *f, u32 domain, u32 rtr)
{
struct top_hash_entry *e;
e = f->hash_table[ospf_top_hash(f, domain, 0, rtr, LSA_T_RT)];
- return find_matching_rt(e, domain, rtr);
+ return find_matching_rt3(e, domain, rtr);
}
struct top_hash_entry *
-ospf_hash_find_rt_next(struct top_hash_entry *e)
+ospf_hash_find_rt3_next(struct top_hash_entry *e)
{
- return find_matching_rt(e->next, e->domain, e->lsa.rt);
+ return find_matching_rt3(e->next, e->domain, e->lsa.rt);
}
-#endif
+/* In OSPFv2, we don't know Router ID when looking for network LSAs.
+ There should be just one, so we find any match. */
+struct top_hash_entry *
+ospf_hash_find_net2(struct top_graph *f, u32 domain, u32 id)
+{
+ struct top_hash_entry *e;
+ e = f->hash_table[ospf_top_hash(f, domain, id, 0, LSA_T_NET)];
+
+ while (e && (e->lsa.id != id || e->lsa_type != LSA_T_NET ||
+ e->domain != domain || e->lsa_body == NULL))
+ e = e->next;
+
+ return e;
+}
struct top_hash_entry *
@@ -1786,21 +1899,23 @@ ospf_hash_get(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type)
ee = f->hash_table + ospf_top_hash(f, domain, lsa, rtr, type);
e = *ee;
- while (e && (e->lsa.id != lsa || e->lsa.rt != rtr || e->lsa.type != type || e->domain != domain))
+ while (e && (e->lsa.id != lsa || e->lsa.rt != rtr ||
+ e->lsa_type != type || e->domain != domain))
e = e->next;
if (e)
return e;
e = sl_alloc(f->hash_slab);
+ bzero(e, sizeof(struct top_hash_entry));
+
e->color = OUTSPF;
e->dist = LSINFINITY;
- e->nhs = NULL;
- e->lb = IPA_NONE;
+ e->lsa.type_raw = type;
e->lsa.id = lsa;
e->lsa.rt = rtr;
- e->lsa.type = type;
- e->lsa_body = NULL;
+ e->lsa.sn = LSA_ZEROSEQNO;
+ e->lsa_type = type;
e->domain = domain;
e->next = *ee;
*ee = e;
@@ -1812,8 +1927,8 @@ ospf_hash_get(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type)
void
ospf_hash_delete(struct top_graph *f, struct top_hash_entry *e)
{
- struct top_hash_entry **ee = f->hash_table +
- ospf_top_hash(f, e->domain, e->lsa.id, e->lsa.rt, e->lsa.type);
+ struct top_hash_entry **ee = f->hash_table +
+ ospf_top_hash(f, e->domain, e->lsa.id, e->lsa.rt, e->lsa_type);
while (*ee)
{
@@ -1853,7 +1968,7 @@ ospf_dump_lsa(struct top_hash_entry *he, struct proto *p)
rr = (struct ospf_lsa_rt_link *) (rt + 1);
for (i = 0; i < lsa_rt_items(&he->lsa); i++)
- OSPF_TRACE(D_EVENTS, " - %1x %-1R %-1R %5u",
+ OSPF_TRACE(D_EVENTS, " - %1x %-1R %-1R %5u",
rr[i].type, rr[i].id, rr[i].data, rr[i].metric);
break;
@@ -1862,7 +1977,7 @@ ospf_dump_lsa(struct top_hash_entry *he, struct proto *p)
rts = (u32 *) (ln + 1);
for (i = 0; i < lsa_net_items(&he->lsa); i++)
- OSPF_TRACE(D_EVENTS, " - %-1R", rts[i]);
+ OSPF_TRACE(D_EVENTS, " - %-1R", rts[i]);
break;
default:
@@ -1873,7 +1988,7 @@ ospf_dump_lsa(struct top_hash_entry *he, struct proto *p)
void
ospf_top_dump(struct top_graph *f, struct proto *p)
{
- unsigned int i;
+ uint i;
OSPF_TRACE(D_EVENTS, "Hash entries: %d", f->hash_entries);
for (i = 0; i < f->hash_size; i++)
@@ -1884,30 +1999,3 @@ ospf_top_dump(struct top_graph *f, struct proto *p)
}
}
*/
-
-/* This is very inefficient, please don't call it often */
-
-/* I should also test for every LSA if it's in some link state
- * retransmission list for every neighbor. I will not test it.
- * It could happen that I'll receive some strange ls ack's.
- */
-
-int
-can_flush_lsa(struct proto_ospf *po)
-{
- struct ospf_iface *ifa;
- struct ospf_neighbor *n;
-
- WALK_LIST(ifa, po->iface_list)
- {
- WALK_LIST(n, ifa->neigh_list)
- {
- if ((n->state == NEIGHBOR_EXCHANGE) || (n->state == NEIGHBOR_LOADING))
- return 0;
-
- break;
- }
- }
-
- return 1;
-}