summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/bird.sgml15
-rw-r--r--lib/birdlib.h7
-rw-r--r--nest/proto.c8
-rw-r--r--nest/protocol.h1
-rw-r--r--nest/rt-table.c48
-rw-r--r--proto/bgp/bgp.c5
-rw-r--r--proto/bgp/bgp.h8
-rw-r--r--proto/ospf/config.Y2
-rw-r--r--proto/ospf/dbdes.c35
-rw-r--r--proto/ospf/lsack.c7
-rw-r--r--proto/ospf/lsalib.c95
-rw-r--r--proto/ospf/lsalib.h9
-rw-r--r--proto/ospf/lsreq.c2
-rw-r--r--proto/ospf/lsupd.c9
-rw-r--r--proto/ospf/neighbor.c10
-rw-r--r--proto/ospf/ospf.c6
-rw-r--r--proto/ospf/ospf.h90
-rw-r--r--proto/ospf/topology.c49
18 files changed, 322 insertions, 84 deletions
diff --git a/doc/bird.sgml b/doc/bird.sgml
index b3ac38b0..e531da40 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -3491,6 +3491,11 @@ protocol ospf [v2|v3] <name> {
Specifies interval in seconds between retransmissions of unacknowledged
updates. Default value is 5.
+ <tag><label id="ospf-transmit-delay">transmit delay <M>num</M></tag>
+ Specifies estimated transmission delay of link state updates send over
+ the interface. The value is added to LSA age of LSAs propagated through
+ it. Default value is 1.
+
<tag><label id="ospf-priority">priority <M>num</M></tag>
On every multiple access network (e.g., the Ethernet) Designated Router
and Backup Designated router are elected. These routers have some special
@@ -3511,16 +3516,6 @@ protocol ospf [v2|v3] &lt;name&gt; {
<m/dead/ seconds, it will consider the neighbor down. If both directives
<cf/dead count/ and <cf/dead/ are used, <cf/dead/ has precedence.
- <tag><label id="ospf-secondary">secondary <M>switch</M></tag>
- On BSD systems, older versions of BIRD supported OSPFv2 only for the
- primary IP address of an interface, other IP ranges on the interface
- were handled as stub networks. Since v1.4.1, regular operation on
- secondary IP addresses is supported, but disabled by default for
- compatibility. This option allows to enable it. The option is a
- transitional measure, will be removed in the next major release as the
- behavior will be changed. On Linux systems, the option is irrelevant, as
- operation on non-primary addresses is already the regular behavior.
-
<tag><label id="ospf-rx-buffer">rx buffer <M>num</M></tag>
This option allows to specify the size of buffers used for packet
processing. The buffer size should be bigger than maximal size of any
diff --git a/lib/birdlib.h b/lib/birdlib.h
index 428b3209..7cd78032 100644
--- a/lib/birdlib.h
+++ b/lib/birdlib.h
@@ -56,6 +56,13 @@ static inline int u64_cmp(u64 i1, u64 i2)
#define BIT32_CLR(b,p) ((b)[(p)/32] &= ~BIT32_VAL(p))
#define BIT32_ZERO(b,l) memset((b), 0, (l)/8)
+/* The same, but counting bits from MSB */
+#define BIT32R_VAL(p) ((((u32) 1) << 31) >> ((p) % 32))
+#define BIT32R_TEST(b,p) ((b)[(p)/32] & BIT32R_VAL(p))
+#define BIT32R_SET(b,p) ((b)[(p)/32] |= BIT32R_VAL(p))
+#define BIT32R_CLR(b,p) ((b)[(p)/32] &= ~BIT32R_VAL(p))
+#define BIT32R_ZERO(b,l) memset((b), 0, (l)/8)
+
#ifndef NULL
#define NULL ((void *) 0)
#endif
diff --git a/nest/proto.c b/nest/proto.c
index fadce6c7..d4a333d0 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -1693,11 +1693,11 @@ channel_show_stats(struct channel *c)
struct proto_stats *s = &c->stats;
if (c->in_keep_filtered)
- cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported",
- s->imp_routes, s->filt_routes, s->exp_routes);
+ cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported, %u preferred",
+ s->imp_routes, s->filt_routes, s->exp_routes, s->pref_routes);
else
- cli_msg(-1006, " Routes: %u imported, %u exported",
- s->imp_routes, s->exp_routes);
+ cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred",
+ s->imp_routes, s->exp_routes, s->pref_routes);
cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
diff --git a/nest/protocol.h b/nest/protocol.h
index 7f539aef..6c04071b 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -134,6 +134,7 @@ struct proto_stats {
/* Import - from protocol to core */
u32 imp_routes; /* Number of routes successfully imported to the (adjacent) routing table */
u32 filt_routes; /* Number of routes rejected in import filter but kept in the routing table */
+ u32 pref_routes; /* Number of routes selected as best in the (adjacent) routing table */
u32 imp_updates_received; /* Number of route updates received */
u32 imp_updates_invalid; /* Number of route updates rejected as invalid */
u32 imp_updates_filtered; /* Number of route updates rejected by filters */
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 61ddb8c0..a1900532 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -564,33 +564,47 @@ rt_notify_basic(struct channel *c, net *net, rte *new0, rte *old0, int refeed)
* and the end of refeed - if a newly filtered route disappears during this
* period, proper withdraw is not sent (because old would be also filtered)
* and the route is not refeeded (because it disappeared before that).
- * Therefore, we also do not try to run the filter on old routes that are
- * older than the last filter change.
+ * This is handled below as a special case.
*/
if (new)
new = export_filter(c, new, &new_free, 0);
- if (old && !(refeed || (old->lastmod <= c->last_tx_filter_change)))
+ if (old && !refeed)
old = export_filter(c, old, &old_free, 1);
if (!new && !old)
{
/*
* As mentioned above, 'old' value may be incorrect in some race conditions.
- * We generally ignore it with the exception of withdraw to pipe protocol.
- * In that case we rather propagate unfiltered withdraws regardless of
- * export filters to ensure that when a protocol is flushed, its routes are
- * removed from all tables. Possible spurious unfiltered withdraws are not
- * problem here as they are ignored if there is no corresponding route at
- * the other end of the pipe. We directly call rt_notify() hook instead of
+ * We generally ignore it with two exceptions:
+ *
+ * First, withdraw to pipe protocol. In that case we rather propagate
+ * unfiltered withdraws regardless of export filters to ensure that when a
+ * protocol is flushed, its routes are removed from all tables. Possible
+ * spurious unfiltered withdraws are not problem here as they are ignored if
+ * there is no corresponding route at the other end of the pipe.
+ *
+ * Second, recent filter change. If old route is older than filter change,
+ * then it was previously evaluated by a different filter and we do not know
+ * whether it was really propagated. In that case we rather send spurious
+ * withdraw than do nothing and possibly cause phantom routes.
+ *
+ * In both cases wqe directly call rt_notify() hook instead of
* do_rt_notify() to avoid logging and stat counters.
*/
+ int pipe_withdraw = 0, filter_change = 0;
#ifdef CONFIG_PIPE
- if ((p->proto == &proto_pipe) && !new0 && (p != old0->sender->proto))
- p->rt_notify(p, c, net, NULL, old0);
+ pipe_withdraw = (p->proto == &proto_pipe) && !new0;
#endif
+ filter_change = old0 && (old0->lastmod <= c->last_tx_filter_change);
+
+ if ((pipe_withdraw || filter_change) && (p != old0->sender->proto))
+ {
+ c->stats.exp_withdraws_accepted++;
+ p->rt_notify(p, c, net, NULL, old0);
+ }
return;
}
@@ -900,8 +914,16 @@ rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old,
if (!old && !new)
return;
- if ((type == RA_OPTIMAL) && tab->hostcache)
- rt_notify_hostcache(tab, net);
+ if (type == RA_OPTIMAL)
+ {
+ if (new)
+ new->sender->stats.pref_routes++;
+ if (old)
+ old->sender->stats.pref_routes--;
+
+ if (tab->hostcache)
+ rt_notify_hostcache(tab, net);
+ }
struct channel *c; node *n;
WALK_LIST2(c, n, tab->channels, table_node)
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 43f9f134..8dedde9f 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -1692,6 +1692,11 @@ bgp_channel_cleanup(struct channel *C)
if (c->igp_table_ip6)
rt_unlock_table(c->igp_table_ip6);
+
+ c->index = 0;
+
+ /* Cleanup rest of bgp_channel starting at pool field */
+ memset(&(c->pool), 0, sizeof(struct bgp_channel) - OFFSETOF(struct bgp_channel, pool));
}
static inline struct bgp_channel_config *
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index 2b60f90f..cfc88d8e 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -299,12 +299,16 @@ struct bgp_channel {
/* Rest are BGP specific data */
struct bgp_channel_config *cf;
- pool *pool; /* XXXX */
u32 afi;
u32 index;
const struct bgp_af_desc *desc;
+ rtable *igp_table_ip4; /* Table for recursive IPv4 next hop lookups */
+ rtable *igp_table_ip6; /* Table for recursive IPv6 next hop lookups */
+
+ /* Rest are zeroed when down */
+ pool *pool;
HASH(struct bgp_bucket) bucket_hash; /* Hash table of route buckets */
struct bgp_bucket *withdraw_bucket; /* Withdrawn routes */
list bucket_queue; /* Queue of buckets to send (struct bgp_bucket) */
@@ -312,8 +316,6 @@ struct bgp_channel {
HASH(struct bgp_prefix) prefix_hash; /* Prefixes to be sent */
slab *prefix_slab; /* Slab holding prefix nodes */
- rtable *igp_table_ip4; /* Table for recursive IPv4 next hop lookups */
- rtable *igp_table_ip6; /* Table for recursive IPv6 next hop lookups */
ip_addr next_hop_addr; /* Local address for NEXT_HOP attribute */
ip_addr link_addr; /* Link-local version of next_hop_addr */
diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y
index b89584e1..2ec9babe 100644
--- a/proto/ospf/config.Y
+++ b/proto/ospf/config.Y
@@ -199,7 +199,7 @@ CF_KEYWORDS(ELIGIBLE, POLL, NETWORKS, HIDDEN, VIRTUAL, CHECK, LINK, ONLY, BFD)
CF_KEYWORDS(RX, BUFFER, LARGE, NORMAL, STUBNET, HIDDEN, SUMMARY, TAG, EXTERNAL)
CF_KEYWORDS(WAIT, DELAY, LSADB, ECMP, LIMIT, WEIGHT, NSSA, TRANSLATOR, STABILITY)
CF_KEYWORDS(GLOBAL, LSID, ROUTER, SELF, INSTANCE, REAL, NETMASK, TX, PRIORITY, LENGTH)
-CF_KEYWORDS(SECONDARY, MERGE, LSA, SUPPRESSION, MULTICAST, RFC5838)
+CF_KEYWORDS(MERGE, LSA, SUPPRESSION, MULTICAST, RFC5838)
%type <ld> lsadb_args
%type <i> ospf_variant ospf_af_mc nbma_eligible
diff --git a/proto/ospf/dbdes.c b/proto/ospf/dbdes.c
index 019aff04..a1559782 100644
--- a/proto/ospf/dbdes.c
+++ b/proto/ospf/dbdes.c
@@ -127,7 +127,7 @@ ospf_prepare_dbdes(struct ospf_proto *p, struct ospf_neighbor *n)
{
struct ospf_dbdes2_packet *ps = (void *) pkt;
ps->iface_mtu = htons(iface_mtu);
- ps->options = ifa->oa->options;
+ ps->options = ifa->oa->options | OPT_O;
ps->imms = 0; /* Will be set later */
ps->ddseq = htonl(n->dds);
length = sizeof(struct ospf_dbdes2_packet);
@@ -162,7 +162,8 @@ ospf_prepare_dbdes(struct ospf_proto *p, struct ospf_neighbor *n)
}
if ((en->lsa.age < LSA_MAXAGE) &&
- lsa_flooding_allowed(en->lsa_type, en->domain, ifa))
+ lsa_flooding_allowed(en->lsa_type, en->domain, ifa) &&
+ lsa_is_acceptable(en->lsa_type, n, p))
{
lsa_hton_hdr(&(en->lsa), lsas + i);
i++;
@@ -237,6 +238,14 @@ ospf_rxmt_dbdes(struct ospf_proto *p, struct ospf_neighbor *n)
ospf_do_send_dbdes(p, n);
}
+void
+ospf_reset_ldd(struct ospf_proto *p UNUSED, struct ospf_neighbor *n)
+{
+ mb_free(n->ldd_buffer);
+ n->ldd_buffer = NULL;
+ n->ldd_bsize = 0;
+}
+
static int
ospf_process_dbdes(struct ospf_proto *p, struct ospf_packet *pkt, struct ospf_neighbor *n)
{
@@ -341,6 +350,16 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa,
rcv_ddseq = ntohl(ps->ddseq);
}
+ /* Reject packets with non-matching MTU */
+ if ((ifa->type != OSPF_IT_VLINK) &&
+ (rcv_iface_mtu != ifa->iface->mtu) &&
+ (rcv_iface_mtu != 0) && (ifa->iface->mtu != 0))
+ {
+ LOG_PKT("MTU mismatch with nbr %R on %s (remote %d, local %d)",
+ n->rid, ifa->ifname, rcv_iface_mtu, ifa->iface->mtu);
+ return;
+ }
+
switch (n->state)
{
case NEIGHBOR_DOWN:
@@ -356,13 +375,6 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa,
/* fallthrough */
case NEIGHBOR_EXSTART:
- if ((ifa->type != OSPF_IT_VLINK) &&
- (rcv_iface_mtu != ifa->iface->mtu) &&
- (rcv_iface_mtu != 0) &&
- (ifa->iface->mtu != 0))
- LOG_PKT_WARN("MTU mismatch with nbr %R on %s (remote %d, local %d)",
- n->rid, ifa->ifname, rcv_iface_mtu, ifa->iface->mtu);
-
if (((rcv_imms & DBDES_IMMS) == DBDES_IMMS) &&
(n->rid > p->router_id) &&
(plen == ospf_dbdes_hdrlen(p)))
@@ -430,6 +442,7 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa,
if (!(n->myimms & DBDES_M) && !(n->imms & DBDES_M))
{
tm_stop(n->dbdes_timer);
+ ospf_reset_ldd(p, n);
ospf_neigh_sm(n, INM_EXDONE);
break;
}
@@ -453,7 +466,11 @@ ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa,
ospf_send_dbdes(p, n);
if (!(n->myimms & DBDES_M) && !(n->imms & DBDES_M))
+ {
+ /* Use dbdes timer to postpone freeing of Last DBDES packet buffer */
+ tm_start(n->dbdes_timer, n->ifa->deadint S);
ospf_neigh_sm(n, INM_EXDONE);
+ }
}
break;
diff --git a/proto/ospf/lsack.c b/proto/ospf/lsack.c
index 251b5e47..9198dd92 100644
--- a/proto/ospf/lsack.c
+++ b/proto/ospf/lsack.c
@@ -106,6 +106,13 @@ ospf_send_lsack_(struct ospf_proto *p, struct ospf_neighbor *n, int queue)
length = ospf_pkt_hdrlen(p) + i * sizeof(struct ospf_lsa_header);
pkt->length = htons(length);
+ if (queue == ACKL_DIRECT)
+ {
+ OSPF_PACKET(ospf_dump_lsack, pkt, "LSACK packet sent to nbr %R on %s", n->rid, ifa->ifname);
+ ospf_send_to(ifa, n->ip);
+ return;
+ }
+
OSPF_PACKET(ospf_dump_lsack, pkt, "LSACK packet sent via %s", ifa->ifname);
if (ifa->type == OSPF_IT_BCAST)
diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c
index fbfd8d29..e66d3dc0 100644
--- a/proto/ospf/lsalib.c
+++ b/proto/ospf/lsalib.c
@@ -91,6 +91,30 @@ lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa)
}
}
+int
+lsa_is_acceptable(u32 type, struct ospf_neighbor *n, struct ospf_proto *p)
+{
+ if (ospf_is_v2(p))
+ {
+ if (type == LSA_T_NSSA)
+ return !!(n->options & OPT_N);
+
+ if (lsa_is_opaque(type))
+ return !!(n->options & OPT_O);
+
+ return 1;
+ }
+ else
+ {
+ /*
+ * There should be check whether receiving router understands that type
+ * of LSA (for LSA types with U-bit == 0). But as we do not support any
+ * optional LSA types, this is not needed yet.
+ */
+
+ return 1;
+ }
+}
static int
unknown_lsa_type(u32 type)
@@ -105,6 +129,9 @@ unknown_lsa_type(u32 type)
case LSA_T_NSSA:
case LSA_T_LINK:
case LSA_T_PREFIX:
+ case LSA_T_RI_LINK:
+ case LSA_T_RI_AREA:
+ case LSA_T_RI_AS:
return 0;
default:
@@ -112,28 +139,47 @@ unknown_lsa_type(u32 type)
}
}
-#define LSA_V2_TMAX 8
-static const u16 lsa_v2_types[LSA_V2_TMAX] =
- {0, LSA_T_RT, LSA_T_NET, LSA_T_SUM_NET, LSA_T_SUM_RT, LSA_T_EXT, 0, LSA_T_NSSA};
+/* Maps OSPFv2 types to OSPFv3 types */
+static const u16 lsa_v2_types[] = {
+ 0, LSA_T_RT, LSA_T_NET, LSA_T_SUM_NET, LSA_T_SUM_RT, LSA_T_EXT, 0, LSA_T_NSSA,
+ 0, LSA_T_OPAQUE_LINK, LSA_T_OPAQUE_AREA, LSA_T_OPAQUE_AS
+};
+
+/* Maps OSPFv2 opaque types to OSPFv3 function codes */
+static const u16 opaque_lsa_types[] = {
+ [LSA_OT_RI] = LSA_T_RI_,
+};
+
+/* Maps (subset of) OSPFv3 function codes to OSPFv2 opaque types */
+static const u8 opaque_lsa_types_inv[] = {
+ [LSA_T_RI_] = LSA_OT_RI,
+};
+
+#define LOOKUP(a, i) ({ uint _i = (i); (_i < ARRAY_SIZE(a)) ? a[_i] : 0; })
void
-lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain)
+lsa_get_type_domain_(u32 type, u32 id, struct ospf_iface *ifa, u32 *otype, u32 *domain)
{
if (ospf_is_v2(ifa->oa->po))
{
- itype = itype & LSA_T_V2_MASK;
- itype = (itype < LSA_V2_TMAX) ? lsa_v2_types[itype] : 0;
+ type = type & LSA_T_V2_MASK;
+ type = LOOKUP(lsa_v2_types, type);
+
+ uint code;
+ if (LSA_FUNCTION(type) == LSA_T_OPAQUE_)
+ if (code = LOOKUP(opaque_lsa_types, id >> 24))
+ type = code | LSA_UBIT | LSA_SCOPE(type);
}
else
{
/* For unkown LSAs without U-bit change scope to LSA_SCOPE_LINK */
- if (unknown_lsa_type(itype) && !(itype & LSA_UBIT))
- itype = itype & ~LSA_SCOPE_MASK;
+ if (unknown_lsa_type(type) && !(type & LSA_UBIT))
+ type = type & ~LSA_SCOPE_MASK;
}
- *otype = itype;
+ *otype = type;
- switch (LSA_SCOPE(itype))
+ switch (LSA_SCOPE(type))
{
case LSA_SCOPE_LINK:
*domain = ifa->iface_id;
@@ -150,6 +196,12 @@ lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain)
}
}
+u32
+lsa_get_opaque_type(u32 type)
+{
+ return LOOKUP(opaque_lsa_types_inv, LSA_FUNCTION(type));
+}
+
void
lsa_generate_checksum(struct ospf_lsa_header *lsa, const u8 *body)
@@ -548,6 +600,17 @@ lsa_validate_prefix(struct ospf_lsa_header *lsa, struct ospf_lsa_prefix *body)
return lsa_validate_pxlist(lsa, body->pxcount, sizeof(struct ospf_lsa_prefix), (u8 *) body);
}
+static int
+lsa_validate_ri(struct ospf_lsa_header *lsa UNUSED, struct ospf_lsa_net *body UNUSED)
+{
+ /*
+ * There should be proper validation. But we do not really process RI LSAs, so
+ * we can just accept them like another unknown opaque LSAs.
+ */
+
+ return 1;
+}
+
/**
* lsa_validate - check whether given LSA is valid
@@ -577,6 +640,14 @@ lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body)
case LSA_T_EXT:
case LSA_T_NSSA:
return lsa_validate_ext2(lsa, body);
+ case LSA_T_RI_LINK:
+ case LSA_T_RI_AREA:
+ case LSA_T_RI_AS:
+ return lsa_validate_ri(lsa, body);
+ case LSA_T_OPAQUE_LINK:
+ case LSA_T_OPAQUE_AREA:
+ case LSA_T_OPAQUE_AS:
+ return 1; /* Unknown Opaque LSAs */
default:
return 0; /* Should not happen, unknown LSAs are already rejected */
}
@@ -600,6 +671,10 @@ lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body)
return lsa_validate_link(lsa, body);
case LSA_T_PREFIX:
return lsa_validate_prefix(lsa, body);
+ case LSA_T_RI_LINK:
+ case LSA_T_RI_AREA:
+ case LSA_T_RI_AS:
+ return lsa_validate_ri(lsa, body);
default:
return 1; /* Unknown LSAs are OK in OSPFv3 */
}
diff --git a/proto/ospf/lsalib.h b/proto/ospf/lsalib.h
index fca7faec..af8901ce 100644
--- a/proto/ospf/lsalib.h
+++ b/proto/ospf/lsalib.h
@@ -36,16 +36,21 @@ struct ospf_lsa_rt_walk {
};
-void lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain);
+void lsa_get_type_domain_(u32 type, u32 id, struct ospf_iface *ifa, u32 *otype, u32 *domain);
static inline void lsa_get_type_domain(struct ospf_lsa_header *lsa, struct ospf_iface *ifa, u32 *otype, u32 *domain)
-{ lsa_get_type_domain_(lsa->type_raw, ifa, otype, domain); }
+{ lsa_get_type_domain_(lsa->type_raw, lsa->id, ifa, otype, domain); }
static inline u32 lsa_get_etype(struct ospf_lsa_header *h, struct ospf_proto *p)
{ return ospf_is_v2(p) ? (h->type_raw & LSA_T_V2_MASK) : h->type_raw; }
+/* Assuming OSPFv2 - All U-bit LSAs are mapped to Opaque LSAs */
+static inline int lsa_is_opaque(u32 type)
+{ return !!(type & LSA_UBIT); }
+u32 lsa_get_opaque_type(u32 type);
int lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa);
+int lsa_is_acceptable(u32 type, struct ospf_neighbor *n, struct ospf_proto *p);
void lsa_generate_checksum(struct ospf_lsa_header *lsa, const u8 *body);
u16 lsa_verify_checksum(const void *lsa_n, int lsa_len);
diff --git a/proto/ospf/lsreq.c b/proto/ospf/lsreq.c
index 657c0247..45af7533 100644
--- a/proto/ospf/lsreq.c
+++ b/proto/ospf/lsreq.c
@@ -125,7 +125,7 @@ ospf_receive_lsreq(struct ospf_packet *pkt, struct ospf_iface *ifa,
id = ntohl(lsrs[i].id);
rt = ntohl(lsrs[i].rt);
- lsa_get_type_domain_(ntohl(lsrs[i].type), ifa, &type, &domain);
+ lsa_get_type_domain_(ntohl(lsrs[i].type), id, ifa, &type, &domain);
DBG("Processing requested LSA: Type: %04x, Id: %R, Rt: %R\n", type, id, rt);
diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c
index a98c9098..7318b751 100644
--- a/proto/ospf/lsupd.c
+++ b/proto/ospf/lsupd.c
@@ -171,7 +171,8 @@ ospf_add_flushed_to_lsrt(struct ospf_proto *p, struct ospf_neighbor *n)
WALK_SLIST(en, p->lsal)
if ((en->lsa.age == LSA_MAXAGE) && (en->lsa_body != NULL) &&
- lsa_flooding_allowed(en->lsa_type, en->domain, n->ifa))
+ lsa_flooding_allowed(en->lsa_type, en->domain, n->ifa) &&
+ lsa_is_acceptable(en->lsa_type, n, p))
ospf_lsa_lsrt_up(en, n);
/* If we found any flushed LSA, we send them ASAP */
@@ -287,9 +288,9 @@ ospf_flood_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_neig
if (n == from)
continue;
- /* In OSPFv3, there should be check whether receiving router understand
- that type of LSA (for LSA types with U-bit == 0). But as we do not support
- any optional LSA types, this is not needed yet */
+ /* Check whether optional LSAs are supported by neighbor */
+ if (!lsa_is_acceptable(en->lsa_type, n, p))
+ continue;
/* 13.3 (1d) - add LSA to the link state retransmission list */
ospf_lsa_lsrt_up(en, n);
diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c
index 7ce682b0..c143b130 100644
--- a/proto/ospf/neighbor.c
+++ b/proto/ospf/neighbor.c
@@ -176,8 +176,8 @@ ospf_neigh_chstate(struct ospf_neighbor *n, u8 state)
if (state == NEIGHBOR_EXSTART)
{
- /* First time adjacency */
- if (n->adj == 0)
+ /* First time adjacency attempt */
+ if (old_state < NEIGHBOR_EXSTART)
n->dds = random_u32();
n->dds++;
@@ -608,6 +608,12 @@ dbdes_timer_hook(timer *t)
if ((n->state == NEIGHBOR_EXCHANGE) && (n->myimms & DBDES_MS))
ospf_rxmt_dbdes(p, n);
+
+ if ((n->state > NEIGHBOR_LOADING) && !(n->myimms & DBDES_MS))
+ {
+ ospf_reset_ldd(p, n);
+ tm_stop(n->dbdes_timer);
+ }
}
static void
diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c
index a5e22269..73500604 100644
--- a/proto/ospf/ospf.c
+++ b/proto/ospf/ospf.c
@@ -92,11 +92,13 @@
* - RFC 2328 - main OSPFv2 standard
* - RFC 5340 - main OSPFv3 standard
* - RFC 3101 - OSPFv2 NSSA areas
+ * - RFC 5250 - OSPFv2 Opaque LSAs
* - RFC 5709 - OSPFv2 HMAC-SHA Cryptographic Authentication
* - RFC 5838 - OSPFv3 Support of Address Families
* - RFC 6549 - OSPFv2 Multi-Instance Extensions
* - RFC 6987 - OSPF Stub Router Advertisement
* - RFC 7166 - OSPFv3 Authentication Trailer
+ * - RFC 7770 - OSPF Router Information LSA
*/
#include <stdlib.h>
@@ -239,6 +241,7 @@ ospf_start(struct proto *P)
p->rfc1583 = c->rfc1583;
p->stub_router = c->stub_router;
p->merge_external = c->merge_external;
+ p->instance_id = c->instance_id;
p->asbr = c->asbr;
p->ecmp = c->ecmp;
p->tick = c->tick;
@@ -648,6 +651,9 @@ ospf_reconfigure(struct proto *P, struct proto_config *CF)
if (p->rfc1583 != new->rfc1583)
return 0;
+ if (p->instance_id != new->instance_id)
+ return 0;
+
if (old->abr != new->abr)
return 0;
diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h
index ff55621a..d3f12738 100644
--- a/proto/ospf/ospf.h
+++ b/proto/ospf/ospf.h
@@ -223,6 +223,7 @@ struct ospf_proto
u8 rfc1583; /* RFC1583 compatibility */
u8 stub_router; /* Do not forward transit traffic */
u8 merge_external; /* Should i merge external routes? */
+ u8 instance_id; /* Differentiate between more OSPF instances */
u8 asbr; /* May i originate any ext/NSSA lsa? */
u8 ecmp; /* Maximal number of nexthops in ECMP route, or 0 */
u64 csn64; /* Last used cryptographic sequence number */
@@ -352,7 +353,6 @@ struct ospf_neighbor
u32 rid; /* Router ID */
ip_addr ip; /* IP of it's interface */
u8 priority; /* Priority */
- u8 adj; /* built adjacency? */
u32 options; /* Options received */
/* Entries dr and bdr store IP addresses in OSPFv2 and router IDs in
@@ -466,6 +466,7 @@ struct ospf_neighbor
#define OPT_L_V2 0x0010 /* OSPFv2, link-local signaling, not used */
#define OPT_R 0x0010 /* OSPFv3, originator is active router */
#define OPT_DC 0x0020 /* Related to demand circuits, not used */
+#define OPT_O 0x0040 /* OSPFv2 Opaque LSA (RFC 5250) */
#define OPT_AF 0x0100 /* OSPFv3 Address Families (RFC 5838) */
#define OPT_L_V3 0x0200 /* OSPFv3, link-local signaling */
#define OPT_AT 0x0400 /* OSPFv3, authentication trailer */
@@ -541,25 +542,44 @@ struct ospf_auth3
#define DBDES_IMMS (DBDES_I | DBDES_M | DBDES_MS)
-#define LSA_T_RT 0x2001
-#define LSA_T_NET 0x2002
-#define LSA_T_SUM_NET 0x2003
-#define LSA_T_SUM_RT 0x2004
-#define LSA_T_EXT 0x4005
-#define LSA_T_NSSA 0x2007
-#define LSA_T_LINK 0x0008
-#define LSA_T_PREFIX 0x2009
-
-#define LSA_T_V2_MASK 0x00ff
-
-#define LSA_UBIT 0x8000
-
-#define LSA_SCOPE_LINK 0x0000
-#define LSA_SCOPE_AREA 0x2000
-#define LSA_SCOPE_AS 0x4000
-#define LSA_SCOPE_RES 0x6000
-#define LSA_SCOPE_MASK 0x6000
-#define LSA_SCOPE(type) ((type) & LSA_SCOPE_MASK)
+/* OSPFv3 LSA Types / LSA Function Codes */
+/* https://www.iana.org/assignments/ospfv3-parameters/ospfv3-parameters.xhtml#ospfv3-parameters-3 */
+#define LSA_T_RT 0x2001
+#define LSA_T_NET 0x2002
+#define LSA_T_SUM_NET 0x2003
+#define LSA_T_SUM_RT 0x2004
+#define LSA_T_EXT 0x4005
+#define LSA_T_NSSA 0x2007
+#define LSA_T_LINK 0x0008
+#define LSA_T_PREFIX 0x2009
+#define LSA_T_RI_ 0x000C
+#define LSA_T_RI_LINK 0x800C
+#define LSA_T_RI_AREA 0xA00C
+#define LSA_T_RI_AS 0xC00C
+#define LSA_T_OPAQUE_ 0x1FFF
+#define LSA_T_OPAQUE_LINK 0x9FFF
+#define LSA_T_OPAQUE_AREA 0xBFFF
+#define LSA_T_OPAQUE_AS 0xDFFF
+
+#define LSA_T_V2_OPAQUE_ 0x0009
+#define LSA_T_V2_MASK 0x00ff
+
+/* OSPFv2 Opaque LSA Types */
+/* https://www.iana.org/assignments/ospf-opaque-types/ospf-opaque-types.xhtml#ospf-opaque-types-2 */
+#define LSA_OT_RI 0x04
+
+#define LSA_FUNCTION_MASK 0x1FFF
+#define LSA_FUNCTION(type) ((type) & LSA_FUNCTION_MASK)
+
+#define LSA_UBIT 0x8000
+
+#define LSA_SCOPE_LINK 0x0000
+#define LSA_SCOPE_AREA 0x2000
+#define LSA_SCOPE_AS 0x4000
+#define LSA_SCOPE_RES 0x6000
+#define LSA_SCOPE_MASK 0x6000
+#define LSA_SCOPE(type) ((type) & LSA_SCOPE_MASK)
+#define LSA_SCOPE_ORDER(type) (((type) >> 13) & 0x3)
#define LSA_MAXAGE 3600 /* 1 hour */
@@ -586,9 +606,20 @@ struct ospf_auth3
#define LSA_EXT2_TOS 0x7F000000
#define LSA_EXT2_EBIT 0x80000000
-#define LSA_EXT3_EBIT 0x4000000
-#define LSA_EXT3_FBIT 0x2000000
-#define LSA_EXT3_TBIT 0x1000000
+#define LSA_EXT3_EBIT 0x04000000
+#define LSA_EXT3_FBIT 0x02000000
+#define LSA_EXT3_TBIT 0x01000000
+
+/* OSPF Router Information (RI) TLVs */
+/* https://www.iana.org/assignments/ospf-parameters/ospf-parameters.xhtml#ri-tlv */
+#define LSA_RI_RIC 1
+#define LSA_RI_RFC 2
+
+/* OSPF Router Informational Capability Bits */
+/* https://www.iana.org/assignments/ospf-parameters/ospf-parameters.xhtml#router-informational-capability */
+#define LSA_RIC_GR_CAPABLE 0
+#define LSA_RIC_GR_HELPER 1
+#define LSA_RIC_STUB_ROUTER 2
struct ospf_lsa_header
@@ -731,6 +762,18 @@ struct ospf_lsa_prefix
u32 rest[];
};
+struct ospf_tlv
+{
+#ifdef CPU_BIG_ENDIAN
+ u16 type;
+ u16 length;
+#else
+ u16 length;
+ u16 type;
+#endif
+ u32 data[];
+};
+
static inline uint
lsa_net_count(struct ospf_lsa_header *lsa)
@@ -998,6 +1041,7 @@ uint ospf_hello3_options(struct ospf_packet *pkt);
/* dbdes.c */
void ospf_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n);
void ospf_rxmt_dbdes(struct ospf_proto *p, struct ospf_neighbor *n);
+void ospf_reset_ldd(struct ospf_proto *p, struct ospf_neighbor *n);
void ospf_receive_dbdes(struct ospf_packet *pkt, struct ospf_iface *ifa, struct ospf_neighbor *n);
uint ospf_dbdes3_options(struct ospf_packet *pkt);
diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c
index e58f1375..f9ca7bfc 100644
--- a/proto/ospf/topology.c
+++ b/proto/ospf/topology.c
@@ -224,12 +224,17 @@ ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa
/*
* 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.
+ * old type is just new type masked by 0xff. That holds for most OSPFv2 types,
+ * but we have to fix it for opaque LSAs.
*/
if (ospf_is_v2(p))
+ {
+ if (lsa_is_opaque(en->lsa_type))
+ en->lsa.type_raw = LSA_T_V2_OPAQUE_ + LSA_SCOPE_ORDER(en->lsa_type);
+
lsa_set_options(&en->lsa, lsa_opts);
+ }
mb_free(en->lsa_body);
en->lsa_body = lsa_body;
@@ -273,6 +278,10 @@ ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa)
u16 lsa_blen = p->lsab_used;
u16 lsa_length = sizeof(struct ospf_lsa_header) + lsa_blen;
+ /* For OSPFv2 Opaque LSAs, LS ID consists of Opaque Type and Opaque ID */
+ if (ospf_is_v2(p) && lsa_is_opaque(lsa->type))
+ lsa->id |= (u32) lsa_get_opaque_type(lsa->type) << 24;
+
en = ospf_hash_get(p->gr, lsa->dom, lsa->id, p->router_id, lsa->type);
if (!SNODE_VALID(en))
@@ -1658,6 +1667,41 @@ ospf_originate_prefix_net_lsa(struct ospf_proto *p, struct ospf_iface *ifa)
ifa->pxn_lsa = ospf_originate_lsa(p, &lsa);
}
+
+/*
+ * Router Information LSA handling
+ * Type = LSA_T_RI_AREA, opaque type = LSA_OT_RI
+ */
+
+void
+ospf_add_ric_tlv(struct ospf_proto *p)
+{
+ struct ospf_tlv *ri = lsab_allocz(p, sizeof(struct ospf_tlv) + sizeof(u32));
+ ri->type = LSA_RI_RIC;
+ ri->length = sizeof(struct ospf_tlv) + sizeof(u32);
+
+ BIT32R_SET(ri->data, LSA_RIC_STUB_ROUTER);
+}
+
+void
+ospf_originate_ri_lsa(struct ospf_proto *p, struct ospf_area *oa)
+{
+ struct ospf_new_lsa lsa = {
+ .type = LSA_T_RI_AREA,
+ .dom = oa->areaid,
+ .id = p->instance_id
+ };
+
+ ospf_add_ric_tlv(p);
+
+ ospf_originate_lsa(p, &lsa);
+}
+
+
+/*
+ * Generic topology code
+ */
+
static inline int breaks_minlsinterval(struct top_hash_entry *en)
{ return en && (en->lsa.age < LSA_MAXAGE) && (lsa_inst_age(en) < MINLSINTERVAL); }
@@ -1692,6 +1736,7 @@ ospf_update_topology(struct ospf_proto *p)
ospf_originate_rt_lsa(p, oa);
ospf_originate_prefix_rt_lsa(p, oa);
+ ospf_originate_ri_lsa(p, oa);
oa->update_rt_lsa = 0;
}
}