summaryrefslogtreecommitdiff
path: root/proto/ospf
diff options
context:
space:
mode:
Diffstat (limited to 'proto/ospf')
-rw-r--r--proto/ospf/config.Y30
-rw-r--r--proto/ospf/hello.c37
-rw-r--r--proto/ospf/ospf.c52
-rw-r--r--proto/ospf/ospf.h14
-rw-r--r--proto/ospf/packet.c3
-rw-r--r--proto/ospf/rt.c4
-rw-r--r--proto/ospf/topology.c230
7 files changed, 252 insertions, 118 deletions
diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y
index 7f7d6a31..77ca26cf 100644
--- a/proto/ospf/config.Y
+++ b/proto/ospf/config.Y
@@ -18,6 +18,7 @@ CF_DEFINES
static struct ospf_area_config *this_area;
static struct nbma_node *this_nbma;
static struct area_net_config *this_pref;
+static struct ospf_stubnet_config *this_stubnet;
static void
finish_iface_config(struct ospf_iface_patt *ip)
@@ -38,7 +39,7 @@ CF_KEYWORDS(NEIGHBORS, RFC1583COMPAT, STUB, TICK, COST, RETRANSMIT)
CF_KEYWORDS(HELLO, TRANSMIT, PRIORITY, DEAD, NONBROADCAST, POINTOPOINT, TYPE)
CF_KEYWORDS(NONE, SIMPLE, AUTHENTICATION, STRICT, CRYPTOGRAPHIC)
CF_KEYWORDS(ELIGIBLE, POLL, NETWORKS, HIDDEN, VIRTUAL, LINK)
-CF_KEYWORDS(RX, BUFFER, LARGE, NORMAL)
+CF_KEYWORDS(RX, BUFFER, LARGE, NORMAL, STUBNET, HIDDEN, SUMMARY)
%type <t> opttext
@@ -75,6 +76,7 @@ ospf_area_start: AREA idval '{' {
init_list(&this_area->patt_list);
init_list(&this_area->vlink_list);
init_list(&this_area->net_list);
+ init_list(&this_area->stubnet_list);
}
;
@@ -90,10 +92,36 @@ ospf_area_item:
STUB COST expr { this_area->stub = $3 ; if($3<=0) cf_error("Stub cost must be greater than zero"); }
| STUB bool {if($2) { if(!this_area->stub) this_area->stub=DEFAULT_STUB_COST;}else{ this_area->stub=0;}}
| NETWORKS '{' pref_list '}'
+ | STUBNET ospf_stubnet
| INTERFACE ospf_iface
| ospf_vlink
;
+ospf_stubnet:
+ ospf_stubnet_start '{' ospf_stubnet_opts '}'
+ | ospf_stubnet_start
+ ;
+
+ospf_stubnet_start:
+ prefix {
+ this_stubnet = cfg_allocz(sizeof(struct ospf_stubnet_config));
+ add_tail(&this_area->stubnet_list, NODE this_stubnet);
+ this_stubnet->px = $1;
+ this_stubnet->cost = COST_D;
+ }
+ ;
+
+ospf_stubnet_opts:
+ /* empty */
+ | ospf_stubnet_opts ospf_stubnet_item ';'
+ ;
+
+ospf_stubnet_item:
+ HIDDEN bool { this_stubnet->hidden = $2; }
+ | SUMMARY bool { this_stubnet->summary = $2; }
+ | COST expr { this_stubnet->cost = $2; }
+ ;
+
ospf_vlink:
ospf_vlink_start '{' ospf_vlink_opts '}' { finish_iface_config(OSPF_PATT); }
| ospf_vlink_start
diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c
index c7d20273..45b6b613 100644
--- a/proto/ospf/hello.c
+++ b/proto/ospf/hello.c
@@ -24,16 +24,36 @@ ospf_hello_receive(struct ospf_hello_packet *ps,
mask = ps->netmask;
ipa_ntoh(mask);
- if (((ifa->type != OSPF_IT_VLINK) && (ifa->type != OSPF_IT_PTP)) &&
- ((unsigned) ipa_mklen(mask) != ifa->iface->addr->pxlen))
- {
- log(L_ERR "%s%I%sbad netmask %I.", beg, faddr, rec, mask);
- return;
- }
+ if (ifa->type != OSPF_IT_VLINK)
+ {
+ char *msg = L_WARN "Received HELLO packet %s (%I) is inconsistent "
+ "with the primary address of interface %s.";
+
+ if ((ifa->type != OSPF_IT_PTP) &&
+ !ipa_equal(mask, ipa_mkmask(ifa->iface->addr->pxlen)))
+ {
+ if (!n) log(msg, "netmask", mask, ifa->iface->name);
+ return;
+ }
+
+ /* This check is not specified in RFC 2328, but it is needed
+ * to handle the case when there is more IP networks on one
+ * physical network (which is not handled in RFC 2328).
+ * We allow OSPF on primary IP address only and ignore HELLO packets
+ * with secondary addresses (which are sent for example by Quagga.
+ */
+ if ((ifa->iface->addr->flags & IA_UNNUMBERED) ?
+ !ipa_equal(faddr, ifa->iface->addr->opposite) :
+ !ipa_equal(ipa_and(faddr,mask), ifa->iface->addr->prefix))
+ {
+ if (!n) log(msg, "address", faddr, ifa->iface->name);
+ return;
+ }
+ }
if (ntohs(ps->helloint) != ifa->helloint)
{
- log(L_WARN "%s%I%shello interval mismatch (%d).", beg, faddr, rec,
+ log(L_ERR "%s%I%shello interval mismatch (%d).", beg, faddr, rec,
ntohs(ps->helloint));
return;
}
@@ -205,7 +225,8 @@ ospf_hello_send(timer * timer, int poll, struct ospf_neighbor *dirn)
pkt->netmask = ipa_mkmask(ifa->iface->addr->pxlen);
ipa_hton(pkt->netmask);
- if (ifa->type == OSPF_IT_VLINK) pkt->netmask = IPA_NONE;
+ if ((ifa->type == OSPF_IT_VLINK) || (ifa->type == OSPF_IT_PTP))
+ pkt->netmask = IPA_NONE;
pkt->helloint = ntohs(ifa->helloint);
pkt->options = ifa->oa->opt.byte;
pkt->priority = ifa->priority;
diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c
index 0cab1d7b..c9b5f430 100644
--- a/proto/ospf/ospf.c
+++ b/proto/ospf/ospf.c
@@ -76,6 +76,9 @@
#include <stdlib.h>
#include "ospf.h"
+
+static void ospf_rt_notify(struct proto *p, net * n, rte * new, rte * old UNUSED, ea_list * attrs);
+static void ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a);
static int ospf_rte_better(struct rte *new, struct rte *old);
static int ospf_rte_same(struct rte *new, struct rte *old);
static void ospf_disp(timer *timer);
@@ -124,6 +127,9 @@ ospf_start(struct proto *p)
po->disp_timer->hook = ospf_disp;
po->disp_timer->recurrent = po->tick;
tm_start(po->disp_timer, 1);
+ po->lsab_size = 256;
+ po->lsab_used = 0;
+ po->lsab = mb_alloc(p->pool, po->lsab_size);
init_list(&(po->iface_list));
init_list(&(po->area_list));
fib_init(&po->rtf, p->pool, sizeof(ort), 16, ospf_rt_initort);
@@ -142,6 +148,7 @@ ospf_start(struct proto *p)
oa = mb_allocz(p->pool, sizeof(struct ospf_area));
add_tail(&po->area_list, NODE oa);
po->areano++;
+ oa->ac = ac;
oa->stub = ac->stub;
oa->areaid = ac->areaid;
oa->rt = NULL;
@@ -224,8 +231,10 @@ ospf_init(struct proto_config *c)
p->import_control = ospf_import_control;
p->make_tmp_attrs = ospf_make_tmp_attrs;
p->store_tmp_attrs = ospf_store_tmp_attrs;
+ p->accept_ra_types = RA_OPTIMAL;
p->rt_notify = ospf_rt_notify;
p->if_notify = ospf_iface_notify;
+ p->ifa_notify = ospf_ifa_notify;
p->rte_better = ospf_rte_better;
p->rte_same = ospf_rte_same;
@@ -428,7 +437,7 @@ ospf_shutdown(struct proto *p)
return PS_DOWN;
}
-void
+static void
ospf_rt_notify(struct proto *p, net * n, rte * new, rte * old UNUSED,
ea_list * attrs)
{
@@ -473,6 +482,25 @@ ospf_rt_notify(struct proto *p, net * n, rte * new, rte * old UNUSED,
}
static void
+ospf_ifa_notify(struct proto *p, unsigned flags, struct ifa *a)
+{
+ struct proto_ospf *po = (struct proto_ospf *) p;
+ struct ospf_iface *ifa;
+
+ if ((a->flags & IA_SECONDARY) || (a->flags & IA_UNNUMBERED))
+ return;
+
+ WALK_LIST(ifa, po->iface_list)
+ {
+ if (ifa->iface == a->iface)
+ {
+ schedule_rt_lsa(ifa->oa);
+ return;
+ }
+ }
+}
+
+static void
ospf_get_status(struct proto *p, byte * buf)
{
struct proto_ospf *po = (struct proto_ospf *) p;
@@ -602,9 +630,31 @@ ospf_reconfigure(struct proto *p, struct proto_config *c)
if (!oa)
return 0;
+ oa->ac = newac;
oa->stub = newac->stub;
if (newac->stub && (oa->areaid == 0)) oa->stub = 0;
+ /* Check stubnet_list */
+ struct ospf_stubnet_config *oldsn = HEAD(oldac->stubnet_list);
+ struct ospf_stubnet_config *newsn = HEAD(newac->stubnet_list);
+
+ while (((NODE(oldsn))->next != NULL) && ((NODE(newsn))->next != NULL))
+ {
+ if (!ipa_equal(oldsn->px.addr, newsn->px.addr) ||
+ (oldsn->px.len != newsn->px.len) ||
+ (oldsn->hidden != newsn->hidden) ||
+ (oldsn->summary != newsn->summary) ||
+ (oldsn->cost != newsn->cost))
+ break;
+
+ oldsn = (struct ospf_stubnet_config *)(NODE(oldsn))->next;
+ newsn = (struct ospf_stubnet_config *)(NODE(newsn))->next;
+ }
+
+ /* If there is no change, both pointers should be NULL */
+ if (((NODE(oldsn))->next) != ((NODE(newsn))->next))
+ schedule_rt_lsa(oa);
+
/* Change net_list */
FIB_WALK(&oa->net_fib, nf) /* First check if some networks are deleted */
{
diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h
index fb78af4e..23f21b8e 100644
--- a/proto/ospf/ospf.h
+++ b/proto/ospf/ospf.h
@@ -99,6 +99,14 @@ struct area_net
u32 metric;
};
+struct ospf_stubnet_config
+{
+ node n;
+ struct prefix px;
+ int hidden, summary;
+ u32 cost;
+};
+
struct ospf_area_config
{
node n;
@@ -107,6 +115,7 @@ struct ospf_area_config
list patt_list;
list vlink_list;
list net_list;
+ list stubnet_list;
};
struct obits
@@ -523,6 +532,7 @@ struct ospf_area
{
node n;
u32 areaid;
+ struct ospf_area_config *ac; /* Related area config */
int origrt; /* Rt lsa origination scheduled? */
struct top_hash_entry *rt; /* My own router LSA */
list cand; /* List of candidates for RT calc. */
@@ -550,6 +560,8 @@ struct proto_ospf
int rfc1583; /* RFC1583 compatibility */
int ebit; /* Did I originate any ext lsa? */
struct ospf_area *backbone; /* If exists */
+ void *lsab; /* LSA buffer used when originating router LSAs */
+ int lsab_size, lsab_used;
};
struct ospf_iface_patt
@@ -585,8 +597,6 @@ int ospf_import_control(struct proto *p, rte **new, ea_list **attrs,
struct linpool *pool);
struct ea_list *ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool);
void ospf_store_tmp_attrs(struct rte *rt, struct ea_list *attrs);
-void ospf_rt_notify(struct proto *p, net *n, rte *new, rte *old,
- ea_list * attrs);
void schedule_rt_lsa(struct ospf_area *oa);
void schedule_rtcalc(struct proto_ospf *po);
void schedule_net_lsa(struct ospf_iface *ifa);
diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c
index 23785fe8..783d28ed 100644
--- a/proto/ospf/packet.c
+++ b/proto/ospf/packet.c
@@ -323,6 +323,9 @@ ospf_rx_hook(sock * sk, int size)
return 1;
}
+ /* This is deviation from RFC 2328 - neighbours should be identified by
+ * IP address on broadcast and NBMA networks.
+ */
n = find_neigh(ifa, ntohl(((struct ospf_packet *) ps)->routerid));
if(!n && (ps->type != HELLO_P))
diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c
index f906de92..79b21e6a 100644
--- a/proto/ospf/rt.c
+++ b/proto/ospf/rt.c
@@ -1003,11 +1003,11 @@ again1:
e->pref = p->preference;
DBG("Mod rte type %d - %I/%d via %I on iface %s, met %d\n",
a0.source, nf->fn.prefix, nf->fn.pxlen, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1);
- rte_update(p->table, ne, p, e);
+ rte_update(p->table, ne, p, p, e);
}
else
{
- rte_update(p->table, ne, p, NULL);
+ rte_update(p->table, ne, p, p, NULL);
FIB_ITERATE_PUT(&fit, nftmp);
fib_delete(fib, nftmp);
goto again1;
diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c
index a15d2e35..371856f9 100644
--- a/proto/ospf/topology.c
+++ b/proto/ospf/topology.c
@@ -23,176 +23,198 @@
int ptp_unnumbered_stub_lsa = 0;
static void *
+lsab_alloc(struct proto_ospf *po, unsigned size)
+{
+ unsigned offset = po->lsab_used;
+ po->lsab_used += size;
+ if (po->lsab_used > po->lsab_size)
+ {
+ po->lsab_size = MAX(po->lsab_used, 2 * po->lsab_size);
+ po->lsab = mb_realloc(po->proto.pool, po->lsab, po->lsab_size);
+ }
+ return ((byte *) po->lsab) + offset;
+}
+
+static inline void *
+lsab_allocz(struct proto_ospf *po, unsigned size)
+{
+ void *r = lsab_alloc(po, size);
+ bzero(r, size);
+ return r;
+}
+
+static inline void *
+lsab_flush(struct proto_ospf *po)
+{
+ void *r = mb_alloc(po->proto.pool, po->lsab_size);
+ memcpy(r, po->lsab, po->lsab_used);
+ po->lsab_used = 0;
+ return r;
+}
+
+static int
+configured_stubnet(struct ospf_area *oa, struct ifa *a)
+{
+ struct ospf_stubnet_config *sn;
+ WALK_LIST(sn, oa->ac->stubnet_list)
+ {
+ 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;
+ }
+ }
+ return 0;
+}
+
+static void *
originate_rt_lsa_body(struct ospf_area *oa, u16 * length)
{
struct proto_ospf *po = oa->po;
struct ospf_iface *ifa;
- int j = 0, k = 0;
- u16 i = 0;
+ int i = 0, j = 0, k = 0, bitv = 0;
struct ospf_lsa_rt *rt;
- struct ospf_lsa_rt_link *ln, *ln_after;
+ struct ospf_lsa_rt_link *ln;
struct ospf_neighbor *neigh;
DBG("%s: Originating RT_lsa body for area \"%I\".\n", po->proto.name,
oa->areaid);
-
- WALK_LIST(ifa, po->iface_list)
- {
- if ((ifa->oa == oa) && (ifa->state != OSPF_IS_DOWN))
- {
- i++;
- if ((ifa->type == OSPF_IT_PTP) && (ifa->state == OSPF_IS_PTP) &&
- (ptp_unnumbered_stub_lsa || !(ifa->iface->addr->flags & IA_UNNUMBERED)))
- i++;
- }
- }
- rt = mb_allocz(po->proto.pool, sizeof(struct ospf_lsa_rt) +
- i * sizeof(struct ospf_lsa_rt_link));
+
+ ASSERT(po->lsab_used == 0);
+ rt = lsab_allocz(po, sizeof(struct ospf_lsa_rt));
if (po->areano > 1)
rt->veb.bit.b = 1;
if ((po->ebit) && (!oa->stub))
rt->veb.bit.e = 1;
- ln = (struct ospf_lsa_rt_link *) (rt + 1);
- ln_after = ln + i;
+ rt = NULL; /* buffer might be reallocated later */
WALK_LIST(ifa, po->iface_list)
{
- if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa == oa) && (!EMPTY_LIST(ifa->neigh_list)))
+ int master = 0;
+
+ if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa == oa) &&
+ (!EMPTY_LIST(ifa->neigh_list)))
{
neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list);
if ((neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff))
- rt->veb.bit.v = 1;
+ bitv = 1;
}
if ((ifa->oa != oa) || (ifa->state == OSPF_IS_DOWN))
continue;
- if (ln == ln_after)
- die("LSA space overflow");
+ /* BIRD does not support interface loops */
+ ASSERT(ifa->state != OSPF_IS_LOOP);
- if (ifa->state == OSPF_IS_LOOP)
- {
- ln->type = 3;
- ln->id = ipa_to_u32(ifa->iface->addr->ip);
- ln->data = 0xffffffff;
- ln->metric = 0;
- ln->notos = 0;
- }
- else
- {
- switch (ifa->type)
+ switch (ifa->type)
{
- case OSPF_IT_PTP: /* rfc2328 - pg126 */
+ case OSPF_IT_PTP: /* RFC2328 - 12.4.1.1 */
neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list);
if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL))
{
+ ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link));
ln->type = LSART_PTP;
ln->id = neigh->rid;
+ ln->data = (ifa->iface->addr->flags & IA_UNNUMBERED) ?
+ ifa->iface->index : ipa_to_u32(ifa->iface->addr->ip);
ln->metric = ifa->cost;
ln->notos = 0;
- if (ifa->iface->addr->flags & IA_UNNUMBERED)
- {
- ln->data = ifa->iface->index;
- }
- else
- {
- ln->data = ipa_to_u32(ifa->iface->addr->ip);
- }
- }
- else
- {
- ln--;
- i--; /* No link added */
- }
-
- if ((ifa->state == OSPF_IS_PTP) &&
- (ptp_unnumbered_stub_lsa || !(ifa->iface->addr->flags & IA_UNNUMBERED)))
- {
- ln++;
- if (ln == ln_after)
- die("LSA space overflow");
-
- ln->type = LSART_STUB;
- ln->metric = ifa->cost;
- ln->notos = 0;
- if (ifa->iface->addr->flags & IA_UNNUMBERED)
- {
- ln->id = ipa_to_u32(ifa->iface->addr->opposite);
- ln->data = 0xffffffff;
- }
- else
- {
- ln->data = ipa_to_u32(ipa_mkmask(ifa->iface->addr->pxlen));
- ln->id = ipa_to_u32(ifa->iface->addr->prefix) & ln->data;
- }
+ i++;
+ master = 1;
}
break;
- case OSPF_IT_BCAST:
+
+ case OSPF_IT_BCAST: /* RFC2328 - 12.4.1.2 */
case OSPF_IT_NBMA:
if (ifa->state == OSPF_IS_WAITING)
- {
- ln->type = LSART_STUB;
- ln->data = ipa_to_u32(ipa_mkmask(ifa->iface->addr->pxlen));
- ln->id = ipa_to_u32(ifa->iface->addr->prefix) & ln->data;
- ln->metric = ifa->cost;
- ln->notos = 0;
- }
- else
- {
- j = 0, k = 0;
- WALK_LIST(neigh, ifa->neigh_list)
+ break;
+
+ j = 0, k = 0;
+ WALK_LIST(neigh, ifa->neigh_list)
{
if ((neigh->rid == ifa->drid) && (neigh->state == NEIGHBOR_FULL))
k = 1;
if (neigh->state == NEIGHBOR_FULL)
j = 1;
}
- if (((ifa->state == OSPF_IS_DR) && (j == 1)) || (k == 1))
+
+ if (((ifa->state == OSPF_IS_DR) && (j == 1)) || (k == 1))
{
+ 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->iface->addr->ip);
ln->metric = ifa->cost;
ln->notos = 0;
+ i++;
+ master = 1;
}
- else
- {
- ln->type = LSART_STUB;
- ln->data = ipa_to_u32(ipa_mkmask(ifa->iface->addr->pxlen));
- ln->id = ipa_to_u32(ifa->iface->addr->prefix) & ln->data;
- ln->metric = ifa->cost;
- ln->notos = 0;
- }
- }
break;
- case OSPF_IT_VLINK:
+
+ case OSPF_IT_VLINK: /* RFC2328 - 12.4.1.3 */
neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list);
if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff))
{
+ ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link));
ln->type = LSART_VLNK;
ln->id = neigh->rid;
+ ln->data = ipa_to_u32(ifa->iface->addr->ip);
ln->metric = ifa->cost;
ln->notos = 0;
- }
- else
- {
- ln--;
- i--; /* No link added */
+ i++;
+ master = 1;
}
break;
+
default:
- ln--;
- i--; /* No link added */
log("Unknown interface type %s", ifa->iface->name);
break;
}
- }
- ln++;
+
+ /* Now we will originate stub areas for interfaces addresses */
+ struct ifa *a;
+ WALK_LIST(a, ifa->iface->addrs)
+ {
+ if (((a == ifa->iface->addr) && master) ||
+ (a->flags & IA_SECONDARY) ||
+ (a->flags & IA_UNNUMBERED) ||
+ configured_stubnet(oa, a))
+ continue;
+
+
+ ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt_link));
+ ln->type = LSART_STUB;
+ ln->id = ipa_to_u32(a->prefix);
+ ln->data = ipa_to_u32(ipa_mkmask(a->pxlen));
+ ln->metric = ifa->cost;
+ ln->notos = 0;
+ i++;
+ }
}
+
+ struct ospf_stubnet_config *sn;
+ 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->notos = 0;
+ i++;
+ }
+
+ rt = po->lsab;
rt->links = i;
- *length = i * sizeof(struct ospf_lsa_rt_link) + sizeof(struct ospf_lsa_rt) +
- sizeof(struct ospf_lsa_header);
- return rt;
+ rt->veb.bit.v = bitv;
+ *length = po->lsab_used + sizeof(struct ospf_lsa_header);
+ return lsab_flush(po);
}
/**