summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/ospf6d/rde.c310
1 files changed, 230 insertions, 80 deletions
diff --git a/usr.sbin/ospf6d/rde.c b/usr.sbin/ospf6d/rde.c
index bd801e00191..e2c99c4d0a8 100644
--- a/usr.sbin/ospf6d/rde.c
+++ b/usr.sbin/ospf6d/rde.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rde.c,v 1.21 2009/02/19 22:05:32 stsp Exp $ */
+/* $OpenBSD: rde.c,v 1.22 2009/02/19 22:17:07 stsp Exp $ */
/*
* Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org>
@@ -63,7 +63,16 @@ struct lsa *rde_asext_put(struct rroute *);
struct lsa *orig_asext_lsa(struct rroute *, u_int16_t);
struct lsa *orig_sum_lsa(struct rt_node *, struct area *, u_int8_t, int);
-struct lsa *orig_intra_lsa_net(struct area *, struct iface *);
+struct lsa *orig_intra_lsa_net(struct iface *, struct vertex *);
+struct lsa *orig_intra_lsa_rtr(struct area *, struct vertex *);
+void orig_intra_area_prefix_lsas(struct area *);
+void append_prefix_lsa(struct lsa **, u_int16_t *,
+ struct lsa_prefix *);
+int link_lsa_from_full_nbr(struct lsa *, struct iface *);
+
+/* A 32-bit value != any ifindex.
+ * We assume ifindex is bound by [1, USHRT_MAX] inclusive. */
+#define LS_ID_INTRA_RTR 0x01000000
/* Tree of prefixes with global scope on given a link,
* see orig_intra_lsa_*() */
@@ -74,9 +83,7 @@ struct prefix_node {
RB_HEAD(prefix_tree, prefix_node);
RB_PROTOTYPE(prefix_tree, prefix_node, entry, prefix_compare);
int prefix_compare(struct prefix_node *, struct prefix_node *);
-void prefix_tree_add_net(struct prefix_tree *, struct lsa_link *);
-void append_prefix_lsas(struct lsa **, u_int16_t *, u_int16_t *,
- struct prefix_tree *);
+void prefix_tree_add(struct prefix_tree *, struct lsa_link *);
struct ospfd_conf *rdeconf = NULL, *nconf = NULL;
struct imsgbuf *ibuf_ospfe;
@@ -404,15 +411,6 @@ rde_dispatch_imsg(int fd, short event, void *bula)
if (nbr->self) {
lsa_merge(nbr, lsa, v);
/* lsa_merge frees the right lsa */
-
- if (lsa->hdr.type == htons(LSA_TYPE_NETWORK)) {
- struct lsa *intra;
- intra = orig_intra_lsa_net(nbr->area,
- nbr->iface);
- if (intra)
- lsa_merge(nbr, intra, NULL);
- }
-
break;
}
@@ -1174,18 +1172,51 @@ rde_summary_update(struct rt_node *rte, struct area *area)
*/
struct lsa *
-orig_intra_lsa_net(struct area *area, struct iface *iface)
+orig_intra_lsa_net(struct iface *iface, struct vertex *old)
{
struct lsa *lsa;
struct vertex *v;
- struct rde_nbr *nbr;
+ struct area *area;
+ struct prefix_node *node;
struct prefix_tree tree;
u_int16_t len;
u_int16_t numprefix;
+ if ((area = area_find(rdeconf, iface->area_id)) == NULL)
+ fatalx("interface lost area");
+
log_debug("orig_intra_lsa_net: area %s, interface %s",
inet_ntoa(area->id), iface->name);
+ RB_INIT(&tree);
+
+ if (iface->state & IF_STA_DR) {
+ RB_FOREACH(v, lsa_tree, &iface->lsa_tree) {
+ if (v->type != LSA_TYPE_LINK)
+ continue;
+ if (link_lsa_from_full_nbr(v->lsa, iface))
+ prefix_tree_add(&tree, &v->lsa->data.link);
+ }
+ if (RB_EMPTY(&tree)) {
+ /* There are no adjacent neighbors on link.
+ * If a copy of this LSA already exists in DB,
+ * it needs to be flushed. orig_intra_lsa_rtr()
+ * will take care of prefixes configured on
+ * this interface. */
+ if (!old)
+ return NULL;
+ } else {
+ /* Add our own prefixes configured for this link. */
+ v = lsa_find(iface, htons(LSA_TYPE_LINK),
+ htonl(iface->ifindex), rde_router_id());
+ if (v)
+ prefix_tree_add(&tree, &v->lsa->data.link);
+ }
+ /* Continue only if a copy of this LSA already exists in DB.
+ * It needs to be flushed. */
+ } else if (!old)
+ return NULL;
+
len = sizeof(struct lsa_hdr) + sizeof(struct lsa_intra_prefix);
if ((lsa = calloc(1, len)) == NULL)
fatal("orig_intra_lsa_net");
@@ -1194,53 +1225,22 @@ orig_intra_lsa_net(struct area *area, struct iface *iface)
lsa->data.pref_intra.ref_lsid = htonl(iface->ifindex);
lsa->data.pref_intra.ref_adv_rtr = rdeconf->rtr_id.s_addr;
- /* Build an RB tree of all global prefixes contained
- * in this interface's link LSAs. This makes it easy
- * to eliminate duplicates. */
- RB_INIT(&tree);
- RB_FOREACH(v, lsa_tree, &iface->lsa_tree) {
- if (v->lsa->hdr.type != htons(LSA_TYPE_LINK))
- continue;
-
- /* Make sure advertising router is adjacent... */
- LIST_FOREACH(nbr, &area->nbr_list, entry) {
- if (v->lsa->hdr.adv_rtr == nbr->id.s_addr)
- break;
- }
- if (!nbr) {
- fatalx("orig_intra_lsa_net: cannot find neighbor");
- free(lsa);
- return (NULL);
- }
- if (nbr->state < NBR_STA_2_WAY)
- continue;
-
- /* ... and that the LSA's link state ID matches
- * the neighbour's interface ID. */
- if (ntohl(v->lsa->hdr.ls_id) != nbr->iface_id)
- continue;
-
- prefix_tree_add_net(&tree, &v->lsa->data.link);
+ numprefix = 0;
+ RB_FOREACH(node, prefix_tree, &tree) {
+ append_prefix_lsa(&lsa, &len, node->prefix);
+ numprefix++;
}
- if (RB_EMPTY(&tree)) {
- free(lsa);
- return NULL;
- }
-
- append_prefix_lsas(&lsa, &len, &numprefix, &tree);
- if (lsa == NULL)
- fatalx("orig_intra_lsa_net: failed to append LSAs");
-
lsa->data.pref_intra.numprefix = htons(numprefix);
while (!RB_EMPTY(&tree))
free(RB_REMOVE(prefix_tree, &tree, RB_ROOT(&tree)));
/* LSA header */
- lsa->hdr.age = htons(DEFAULT_AGE);
+ /* If numprefix is zero, originate with MAX_AGE to flush LSA. */
+ lsa->hdr.age = numprefix == 0 ? htons(MAX_AGE) : htons(DEFAULT_AGE);
lsa->hdr.type = htons(LSA_TYPE_INTRA_A_PREFIX);
- lsa->hdr.ls_id = 0; /* TODO: fragmentation */
+ lsa->hdr.ls_id = htonl(iface->ifindex);
lsa->hdr.adv_rtr = rdeconf->rtr_id.s_addr;
lsa->hdr.seq_num = htonl(INIT_SEQ_NUM);
lsa->hdr.len = htons(len);
@@ -1252,41 +1252,29 @@ orig_intra_lsa_net(struct area *area, struct iface *iface)
/* Prefix LSAs have variable size. We have to be careful to copy the right
* amount of bytes, and to realloc() the right amount of memory. */
void
-append_prefix_lsas(struct lsa **lsa, u_int16_t *len, u_int16_t *numprefix,
- struct prefix_tree *tree)
+append_prefix_lsa(struct lsa **lsa, u_int16_t *len, struct lsa_prefix *prefix)
{
- struct prefix_node *node;
struct lsa_prefix *copy;
unsigned int lsa_prefix_len;
unsigned int new_len;
char *new_lsa;
- *numprefix = 0;
+ lsa_prefix_len = sizeof(struct lsa_prefix)
+ + LSA_PREFIXSIZE(prefix->prefixlen);
- RB_FOREACH(node, prefix_tree, tree) {
- lsa_prefix_len = sizeof(struct lsa_prefix)
- + LSA_PREFIXSIZE(node->prefix->prefixlen);
+ new_len = *len + lsa_prefix_len;
- new_len = *len + lsa_prefix_len;
-
- /* Make sure we have enough space for this prefix. */
- if ((new_lsa = realloc(*lsa, new_len)) == NULL) {
- fatalx("append_prefix_lsas");
- free(*lsa);
- *lsa = NULL;
- *len = 0;
- return;
- }
+ /* Make sure we have enough space for this prefix. */
+ if ((new_lsa = realloc(*lsa, new_len)) == NULL)
+ fatalx("append_prefix_lsa");
- /* Append prefix to LSA. */
- copy = (struct lsa_prefix *)(new_lsa + *len);
- memcpy(copy, node->prefix, lsa_prefix_len);
- copy->metric = 0;
+ /* Append prefix to LSA. */
+ copy = (struct lsa_prefix *)(new_lsa + *len);
+ memcpy(copy, prefix, lsa_prefix_len);
+ copy->metric = 0;
- *lsa = (struct lsa *)new_lsa;
- *len = new_len;
- (*numprefix)++;
- }
+ *lsa = (struct lsa *)new_lsa;
+ *len = new_len;
}
int
@@ -1313,7 +1301,7 @@ prefix_compare(struct prefix_node *a, struct prefix_node *b)
}
void
-prefix_tree_add_net(struct prefix_tree *tree, struct lsa_link *lsa)
+prefix_tree_add(struct prefix_tree *tree, struct lsa_link *lsa)
{
struct prefix_node *old;
struct prefix_node *new;
@@ -1351,6 +1339,168 @@ prefix_tree_add_net(struct prefix_tree *tree, struct lsa_link *lsa)
RB_GENERATE(prefix_tree, prefix_node, entry, prefix_compare)
+/* Return non-zero if Link LSA was originated from an adjacent neighbor. */
+int
+link_lsa_from_full_nbr(struct lsa *lsa, struct iface *iface)
+{
+ struct rde_nbr *nbr;
+ struct area *area;
+
+ if ((area = area_find(rdeconf, iface->area_id)) == NULL)
+ fatalx("interface lost area");
+
+ LIST_FOREACH(nbr, &area->nbr_list, entry) {
+ if (nbr->self || nbr->iface->ifindex != iface->ifindex)
+ continue;
+ if (lsa->hdr.adv_rtr == nbr->id.s_addr)
+ break;
+ }
+ if (!nbr)
+ return 0;
+
+ if (nbr->state & NBR_STA_FULL &&
+ ntohl(lsa->hdr.ls_id) == nbr->iface_id)
+ return 1;
+
+ return 0;
+}
+
+struct lsa *
+orig_intra_lsa_rtr(struct area *area, struct vertex *old)
+{
+ struct lsa *lsa;
+ struct lsa_prefix *lsa_prefix;
+ struct in6_addr *prefix;
+ struct iface *iface;
+ struct iface_addr *ia;
+ struct rde_nbr *nbr;
+ u_int16_t len;
+ u_int16_t numprefix;
+
+ len = sizeof(struct lsa_hdr) + sizeof(struct lsa_intra_prefix);
+ if ((lsa = calloc(1, len)) == NULL)
+ fatal("orig_intra_lsa_net");
+
+ lsa->data.pref_intra.ref_type = htons(LSA_TYPE_ROUTER);
+ lsa->data.pref_intra.ref_lsid = 0;
+ lsa->data.pref_intra.ref_adv_rtr = rde_router_id();
+
+ log_debug("orig_intra_lsa_rtr: area %s", inet_ntoa(area->id));
+
+ numprefix = 0;
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ if (iface->state & IF_STA_DOWN)
+ continue;
+
+ /* Broadcast links with adjacencies are handled
+ * by orig_intra_lsa_net(), ignore. */
+ if (iface->type == IF_TYPE_BROADCAST ||
+ iface->type == IF_TYPE_NBMA) {
+ if (iface->state & IF_STA_WAITING)
+ /* Skip, we're still waiting for
+ * adjacencies to form. */
+ continue;
+
+ LIST_FOREACH(nbr, &area->nbr_list, entry)
+ if (!nbr->self &&
+ nbr->iface->ifindex == iface->ifindex &&
+ nbr->state & NBR_STA_FULL)
+ break;
+ if (nbr)
+ continue;
+ }
+
+ if ((lsa_prefix = calloc(sizeof(*lsa_prefix)
+ + sizeof(struct in6_addr), 1)) == NULL)
+ fatal("orig_intra_lsa_rtr");
+
+ TAILQ_FOREACH(ia, &iface->ifa_list, entry) {
+ if (IN6_IS_ADDR_LINKLOCAL(&ia->addr))
+ continue;
+
+ bzero(lsa_prefix, sizeof(*lsa_prefix)
+ + sizeof(struct in6_addr));
+
+ if (iface->type == IF_TYPE_POINTOMULTIPOINT ||
+ iface->state & IF_STA_LOOPBACK) {
+ lsa_prefix->prefixlen = 128;
+ } else {
+ lsa_prefix->prefixlen = ia->prefixlen;
+ lsa_prefix->metric = htons(iface->metric);
+ }
+
+ if (lsa_prefix->prefixlen == 128)
+ lsa_prefix->options |= OSPF_PREFIX_LA;
+
+ prefix = (struct in6_addr *)(lsa_prefix + 1);
+ inet6applymask(prefix, &ia->addr,
+ lsa_prefix->prefixlen);
+ append_prefix_lsa(&lsa, &len, lsa_prefix);
+ numprefix++;
+ }
+
+ free(lsa_prefix);
+
+ /* TOD: Add prefixes of directly attached hosts, too */
+ /* TOD: Add prefixes for virtual links */
+ }
+
+ /* If no prefixes were included, continue only if a copy of this
+ * LSA already exists in DB. It needs to be flushed. */
+ if (numprefix == 0 && !old) {
+ free(lsa);
+ return NULL;
+ }
+
+ lsa->data.pref_intra.numprefix = htons(numprefix);
+
+ /* LSA header */
+ /* If numprefix is zero, originate with MAX_AGE to flush LSA. */
+ lsa->hdr.age = numprefix == 0 ? htons(MAX_AGE) : htons(DEFAULT_AGE);
+ lsa->hdr.type = htons(LSA_TYPE_INTRA_A_PREFIX);
+ lsa->hdr.ls_id = htonl(LS_ID_INTRA_RTR);
+ lsa->hdr.adv_rtr = rde_router_id();
+ lsa->hdr.seq_num = htonl(INIT_SEQ_NUM);
+ lsa->hdr.len = htons(len);
+ lsa->hdr.ls_chksum = htons(iso_cksum(lsa, len, LS_CKSUM_OFFSET));
+
+ return lsa;
+}
+
+void
+orig_intra_area_prefix_lsas(struct area *area)
+{
+ struct lsa *lsa;
+ struct vertex *old;
+ struct iface *iface;
+ struct vertex key;
+
+ LIST_FOREACH(iface, &area->iface_list, entry) {
+ if (iface->type == IF_TYPE_BROADCAST ||
+ iface->type == IF_TYPE_NBMA) {
+ old = lsa_find(iface, htons(LSA_TYPE_INTRA_A_PREFIX),
+ htonl(iface->ifindex), rde_router_id());
+ lsa = orig_intra_lsa_net(iface, old);
+ if (lsa)
+ lsa_merge(rde_nbr_self(area), lsa, old);
+ }
+ }
+
+ /* XXX: lsa_find() should take an LSA tree as argument,
+ * if you have no iface at hand you cannot use it... */
+ bzero(&key, sizeof(key));
+ key.type = LSA_TYPE_INTRA_A_PREFIX;
+ key.ls_id = LS_ID_INTRA_RTR;
+ key.adv_rtr = ntohl(rde_router_id());
+ old = RB_FIND(lsa_tree, &area->lsa_tree, &key);
+ if (old && old->deleted)
+ old = NULL;
+
+ lsa = orig_intra_lsa_rtr(area, old);
+ if (lsa)
+ lsa_merge(rde_nbr_self(area), lsa, old);
+}
+
struct lsa *
orig_asext_lsa(struct rroute *rr, u_int16_t age)
{