diff options
Diffstat (limited to 'proto/ospf')
-rw-r--r-- | proto/ospf/config.Y | 30 | ||||
-rw-r--r-- | proto/ospf/hello.c | 37 | ||||
-rw-r--r-- | proto/ospf/ospf.c | 52 | ||||
-rw-r--r-- | proto/ospf/ospf.h | 14 | ||||
-rw-r--r-- | proto/ospf/packet.c | 3 | ||||
-rw-r--r-- | proto/ospf/rt.c | 4 | ||||
-rw-r--r-- | proto/ospf/topology.c | 230 |
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); } /** |