0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 17:51:53 +00:00
bird/proto/ospf/topology.c
Martin Mares 0077aab4f9 The code was broken for external /29 to /32 routes. Assuming that you
have one machine publishing a route to 10.1.1.3/32 and another one
publishing a route to 10.1.1.4/32. If the first machine went down the
route to 10.1.1.4/32 was wrongly killed by the old code, leading either
to missing routes or worse to bug()s like "Router parent does not have
next hop" or just segfaults. The patch fixes this but in the long term a
redesign is required here. Note that the patch doesn't worse the
situation, instead it prevents the problems stated. The redesign is
required to handle multiple routes to small subnets properly.

(by Andreas)

Feela, I think that this is at least a good temporary fix, but it's
of course up to you to decide.
2004-05-31 18:16:42 +00:00

691 lines
17 KiB
C

/*
* BIRD -- OSPF Topological Database
*
* (c) 1999 Martin Mares <mj@ucw.cz>
* (c) 1999 - 2000 Ondrej Filip <feela@network.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "nest/bird.h"
#include "lib/string.h"
#include "ospf.h"
#define HASH_DEF_ORDER 6
#define HASH_HI_MARK *4
#define HASH_HI_STEP 2
#define HASH_HI_MAX 16
#define HASH_LO_MARK /5
#define HASH_LO_STEP 2
#define HASH_LO_MIN 8
void *
originate_rt_lsa_body(struct ospf_area *oa, u16 *length, struct proto_ospf *p)
{
struct ospf_iface *ifa;
int j=0,k=0,v=0;
u16 i=0;
struct ospf_lsa_rt *rt;
struct ospf_lsa_rt_link *ln;
struct ospf_neighbor *neigh;
struct top_hash_entry *old;
struct proto_ospf *po=(struct proto_ospf *)p;
DBG("%s: Originating RT_lsa body for area \"%I\".\n", po->proto.name,
oa->areaid);
WALK_LIST (ifa, p->iface_list)
{
if((ifa->an==oa->areaid) && (ifa->state!=OSPF_IS_DOWN))
{
i++;
if(ifa->type==OSPF_IT_VLINK) v=1;
}
}
rt=mb_allocz(p->proto.pool, sizeof(struct ospf_lsa_rt)+
i*sizeof(struct ospf_lsa_rt_link));
if(p->areano>1) rt->veb.bit.b=1;
if((p->ebit)&&(!oa->stub)) rt->veb.bit.e=1;
rt->veb.bit.v=v;
ln=(struct ospf_lsa_rt_link *)(rt+1);
WALK_LIST (ifa, p->iface_list)
{
if((ifa->an!=oa->areaid) || (ifa->state==OSPF_IS_DOWN)) continue;
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)
{
case OSPF_IT_PTP: /* rfc2328 - pg126 */
neigh=(struct ospf_neighbor *)HEAD(ifa->neigh_list);
if((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state==NEIGHBOR_FULL))
{
ln->type=LSART_PTP;
ln->id=neigh->rid;
ln->metric=ifa->cost;
ln->notos=0;
if(ifa->iface->flags && IA_UNNUMBERED)
{
ln->data=ifa->iface->index;
}
else
{
ln->id=ipa_to_u32(ifa->iface->addr->ip);
}
}
else
{
if(ifa->state==OSPF_IS_PTP)
{
ln->type=LSART_STUB;
ln->id=ln->id=ipa_to_u32(ifa->iface->addr->opposite);
ln->metric=ifa->cost;
ln->notos=0;
ln->data=0xffffffff;
}
else
{
i--; /* No link added */
}
}
break;
case OSPF_IT_BCAST:
case OSPF_IT_NBMA:
if(ifa->state==OSPF_IS_WAITING)
{
ln->type=LSART_STUB;
ln->id=ipa_to_u32(ifa->iface->addr->prefix);
ln->data=ipa_to_u32(ipa_mkmask(ifa->iface->addr->pxlen));
ln->metric=ifa->cost;
ln->notos=0;
}
else
{
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))
{
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;
}
else
{
ln->type=LSART_STUB;
ln->id=ipa_to_u32(ifa->iface->addr->prefix);
ln->data=ipa_to_u32(ipa_mkmask(ifa->iface->addr->pxlen));
ln->metric=ifa->cost;
ln->notos=0;
}
}
break;
case OSPF_IT_VLINK: /* FIXME Add virtual links! */
i--;
break;
}
}
if(ifa->type==OSPF_IT_VLINK) v=1;
ln=(ln+1);
}
rt->links=i;
*length=i*sizeof(struct ospf_lsa_rt_link)+sizeof(struct ospf_lsa_rt)+
sizeof(struct ospf_lsa_header);
return rt;
}
void
addifa_rtlsa(struct ospf_iface *ifa)
{
struct ospf_area *oa;
struct proto_ospf *po=ifa->proto;
struct proto *p=&po->proto;
WALK_LIST(NODE oa,po->area_list)
{
if(oa->areaid==ifa->an) break;
}
if(EMPTY_LIST(po->area_list) || (oa->areaid!=ifa->an)) /* New area */
{
bug("Cannot add any area to accepted Interface");
}
else ifa->oa=oa;
}
/**
* 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;
u32 rtid=po->proto.cf->global->router_id;
struct top_hash_entry *en;
void *body;
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, "Originating RT_lsa for area \"%I\".",oa->areaid);
lsa.age=0;
lsa.id=rtid;
lsa.type=LSA_T_RT;
lsa.rt=rtid;
lsa.options=0;
if(oa->rt==NULL)
{
lsa.sn=LSA_INITSEQNO;
}
else
{
lsa.sn=oa->rt->lsa.sn+1;
}
body=originate_rt_lsa_body(oa, &lsa.length, po);
lsasum_calculate(&lsa,body,po);
en=lsa_install_new(&lsa, body, oa);
oa->rt=en;
flood_lsa(NULL,NULL,&oa->rt->lsa,po,NULL,oa,1);
schedule_rtcalc(oa);
oa->origrt=0;
}
void *
originate_net_lsa_body(struct ospf_iface *ifa, u16 *length,
struct proto_ospf *po)
{
u16 i=1;
struct ospf_neighbor *n;
u32 *body;
struct ospf_lsa_net *net;
net=mb_alloc(po->proto.pool,sizeof(u32)*(ifa->fadj+1)+
sizeof(struct ospf_lsa_net));
net->netmask=ipa_mkmask(ifa->iface->addr->pxlen);
body=(u32 *)(net+1);
i=1;
*body=po->proto.cf->global->router_id;
WALK_LIST(n,ifa->neigh_list)
{
if(n->state==NEIGHBOR_FULL)
{
*(body+i)=n->rid;
i++;
}
}
*length=i*sizeof(u32)+sizeof(struct ospf_lsa_header)+
sizeof(struct ospf_lsa_net);
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)
{
struct proto_ospf *po=ifa->proto;
struct ospf_lsa_header lsa;
u32 rtid=po->proto.cf->global->router_id;
struct top_hash_entry *en;
struct proto *p=&po->proto;
void *body;
if(ifa->nlsa&&((ifa->nlsa->inst_t+MINLSINTERVAL)>now)) return;
/*
* It's too early to originate new network LSA. We will
* try to do it next tick
*/
if((ifa->state!=OSPF_IS_DR)||(ifa->fadj==0))
{
if(ifa->nlsa==NULL) return;
OSPF_TRACE(D_EVENTS, "Deleting Net lsa for iface \"%s\".",
ifa->iface->name);
ifa->nlsa->lsa.sn+=1;
ifa->nlsa->lsa.age=LSA_MAXAGE;
flood_lsa(NULL,NULL,&ifa->nlsa->lsa,po,NULL,ifa->oa,0);
s_rem_node(SNODE ifa->nlsa);
ospf_hash_delete(ifa->oa->gr, ifa->nlsa);
schedule_rtcalc(ifa->oa);
ifa->nlsa=NULL;
return ;
}
OSPF_TRACE(D_EVENTS, "Originating Net lsa for iface \"%s\".",
ifa->iface->name);
lsa.age=0;
lsa.id=ipa_to_u32(ifa->iface->addr->ip);
lsa.type=LSA_T_NET;
lsa.rt=rtid;
lsa.options=0;
if(ifa->nlsa==NULL)
{
lsa.sn=LSA_INITSEQNO;
}
else
{
lsa.sn=ifa->nlsa->lsa.sn+1;
}
body=originate_net_lsa_body(ifa, &lsa.length, po);
lsasum_calculate(&lsa,body,po);
ifa->nlsa=lsa_install_new(&lsa, body, ifa->oa);
flood_lsa(NULL,NULL,&ifa->nlsa->lsa,po,NULL,ifa->oa,1);
ifa->orignet=0;
}
static void *
originate_ext_lsa_body(net *n, rte *e, struct proto_ospf *po, struct ea_list *attrs)
{
struct proto *p=&po->proto;
struct ospf_lsa_ext *ext;
struct ospf_lsa_ext_tos *et;
neighbor *nn;
u32 m1 = ea_get_int(attrs, EA_OSPF_METRIC1, LSINFINITY);
u32 m2 = ea_get_int(attrs, EA_OSPF_METRIC2, 10000);
u32 tag = ea_get_int(attrs, EA_OSPF_TAG, 0);
int inas=0;
ext=mb_alloc(p->pool,sizeof(struct ospf_lsa_ext)+
sizeof(struct ospf_lsa_ext_tos));
ext->netmask=ipa_mkmask(n->n.pxlen);
et=(struct ospf_lsa_ext_tos *)(ext+1);
if(m1!=LSINFINITY)
{
et->etos=0;
et->metric=m1;
}
else
{
et->etos=0x80;
et->metric=m2;
}
et->padding=0;
et->tag=tag;
if(ipa_compare(e->attrs->gw,ipa_from_u32(0))!=0)
{
if(find_iface((struct proto_ospf *)p, e->attrs->iface)!=NULL) inas=1;
}
if(!inas) et->fwaddr= ipa_from_u32(0);
else et->fwaddr=e->attrs->gw;
return ext;
}
/**
* max_ext_lsa - calculate the maximum amount of external networks
* possible for the given prefix length.
*
* This is a fix for the previous static use of MAXNETS which did
* only work correct if MAXNETS < possible IPs for given prefix.
* This solution is kind of a hack as there can now only be one
* route for /32 type entries but this is better than the crashes
* I did experience whith close together /32 routes originating
* on different hosts.
*/
int
max_ext_lsa(unsigned pxlen)
{
int i;
for(i=1;pxlen<BITS_PER_IP_ADDRESS;pxlen++,i<<=1)
if(i>=MAXNETS)
return MAXNETS;
return i;
}
/**
* originate_ext_lsa - new route received from nest and filters
* @n: network prefix and mask
* @e: rte
* @po: current instance of OSPF
* @attrs: list of extended attributes
*
* If I receive a message that new route is installed, I try to originate an
* external LSA. The LSA header of such LSA does not contain information about
* prefix length, so if I have to originate multiple LSAs for route with
* different prefixes I try to increment prefix id to find a "free" one.
*
* The function also sets flag ebit. If it's the first time, the new router lsa
* origination is necessary.
*/
void
originate_ext_lsa(net *n, rte *e, struct proto_ospf *po, struct ea_list *attrs)
{
struct ospf_lsa_header lsa;
u32 rtid=po->proto.cf->global->router_id;
struct top_hash_entry *en=NULL;
void *body=NULL;
struct proto *p=&po->proto;
struct ospf_area *oa;
struct ospf_lsa_ext *ext1,*ext2;
int i;
int max;
OSPF_TRACE(D_EVENTS, "Originating Ext lsa for %I/%d.", n->n.prefix,
n->n.pxlen);
lsa.age=0;
lsa.id=ipa_to_u32(n->n.prefix);
lsa.type=LSA_T_EXT;
lsa.rt=rtid;
lsa.sn=LSA_INITSEQNO;
body=originate_ext_lsa_body(n, e, po, attrs);
lsa.length=sizeof(struct ospf_lsa_ext)+sizeof(struct ospf_lsa_ext_tos)+
sizeof(struct ospf_lsa_header);
ext1=body;
max=max_ext_lsa(n->n.pxlen);
oa=HEAD(po->area_list);
for(i=0;i<max;i++)
{
if((en=ospf_hash_find_header(oa->gr, &lsa))!=NULL)
{
ext2=en->lsa_body;
if(ipa_compare(ext1->netmask,ext2->netmask)!=0) lsa.id++;
else break;
}
else break;
}
if(i==max)
{
log("%s: got more routes for one /%d network then %d, ignoring",p->name,
n->n.pxlen,max);
mb_free(body);
return;
}
lsasum_calculate(&lsa,body,po);
WALK_LIST(oa, po->area_list)
{
en=lsa_install_new(&lsa, body, oa);
flood_lsa(NULL,NULL,&en->lsa,po,NULL,oa,1);
body=originate_ext_lsa_body(n, e, po, attrs);
}
mb_free(body);
if(po->ebit==0)
{
po->ebit=1;
WALK_LIST(oa, po->area_list)
{
schedule_rt_lsa(oa);
}
}
}
static void
ospf_top_ht_alloc(struct top_graph *f)
{
f->hash_size = 1 << f->hash_order;
f->hash_mask = f->hash_size - 1;
if (f->hash_order > HASH_HI_MAX - HASH_HI_STEP)
f->hash_entries_max = ~0;
else
f->hash_entries_max = f->hash_size HASH_HI_MARK;
if (f->hash_order < HASH_LO_MIN + HASH_LO_STEP)
f->hash_entries_min = 0;
else
f->hash_entries_min = f->hash_size HASH_LO_MARK;
DBG("Allocating OSPF hash of order %d: %d hash_entries, %d low, %d high\n",
f->hash_order, f->hash_size, f->hash_entries_min, f->hash_entries_max);
f->hash_table = mb_alloc(f->pool, f->hash_size * sizeof(struct top_hash_entry *));
bzero(f->hash_table, f->hash_size * sizeof(struct top_hash_entry *));
}
static inline void
ospf_top_ht_free(struct top_hash_entry **h)
{
mb_free(h);
}
static inline u32
ospf_top_hash_u32(u32 a)
{
/* Shamelessly stolen from IP address hashing in ipv4.h */
a ^= a >> 16;
a ^= a << 10;
return a;
}
static inline unsigned
ospf_top_hash(struct top_graph *f, u32 lsaid, u32 rtrid, u32 type)
{
#if 1 /* Dirty patch to make rt table calculation work. */
return (ospf_top_hash_u32(lsaid) + ospf_top_hash_u32((type==LSA_T_NET) ? lsaid : rtrid) + type) & f->hash_mask;
#else
return (ospf_top_hash_u32(lsaid) + ospf_top_hash_u32(rtrid) + type) & f->hash_mask;
#endif
}
/**
* ospf_top_new - allocated new topology database
* @p: current instance of OSPF
*
* This dynamically hashed structure is often used for keeping LSAs. Mainly
* its used in @ospf_area structure.
*/
struct top_graph *
ospf_top_new(struct proto_ospf *p)
{
struct top_graph *f;
f = mb_allocz(p->proto.pool, sizeof(struct top_graph));
f->pool = p->proto.pool;
f->hash_slab = sl_new(f->pool, sizeof(struct top_hash_entry));
f->hash_order = HASH_DEF_ORDER;
ospf_top_ht_alloc(f);
f->hash_entries = 0;
f->hash_entries_min = 0;
return f;
}
void
ospf_top_free(struct top_graph *f)
{
rfree(f->hash_slab);
ospf_top_ht_free(f->hash_table);
mb_free(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;
oldn = f->hash_size;
oldt = f->hash_table;
DBG("Re-hashing topology hash from order %d to %d\n", f->hash_order, f->hash_order+step);
f->hash_order += step;
ospf_top_ht_alloc(f);
newt = f->hash_table;
for(oldh=0; oldh < oldn; oldh++)
{
e = oldt[oldh];
while (e)
{
x = e->next;
n = newt + ospf_top_hash(f, e->lsa.id, e->lsa.rt, e->lsa.type);
e->next = *n;
*n = e;
e = x;
}
}
ospf_top_ht_free(oldt);
}
struct top_hash_entry *
ospf_hash_find_header(struct top_graph *f, struct ospf_lsa_header *h)
{
return ospf_hash_find(f,h->id,h->rt,h->type);
}
struct top_hash_entry *
ospf_hash_get_header(struct top_graph *f, struct ospf_lsa_header *h)
{
return ospf_hash_get(f,h->id,h->rt,h->type);
}
struct top_hash_entry *
ospf_hash_find(struct top_graph *f, u32 lsa, u32 rtr, u32 type)
{
struct top_hash_entry *e = f->hash_table[ospf_top_hash(f, lsa, rtr, type)];
#if 1 /* Dirty patch to make rt table calculation work. */
if(type==LSA_T_NET)
{
while (e && (e->lsa.id != lsa || e->lsa.type != LSA_T_NET ))
e = e->next;
}
else
{
while (e && (e->lsa.id != lsa || e->lsa.type != type || e->lsa.rt != rtr))
e = e->next;
}
#else
while (e && (e->lsa.id != lsa || e->lsa.rt != rtr || e->lsa.type != type))
e = e->next;
#endif
return e;
}
struct top_hash_entry *
ospf_hash_get(struct top_graph *f, u32 lsa, u32 rtr, u32 type)
{
struct top_hash_entry **ee = f->hash_table + ospf_top_hash(f, lsa, rtr, type);
struct top_hash_entry *e = *ee;
while (e && (e->lsa.id != lsa || e->lsa.rt != rtr || e->lsa.type != type))
e = e->next;
if (e)
return e;
e = sl_alloc(f->hash_slab);
e->lsa.id = lsa;
e->lsa.rt = rtr;
e->lsa.type = type;
e->lsa_body = NULL;
e->nhi=NULL;
e->next=*ee; /* MJ you forgot this :-) */
*ee=e;
if (f->hash_entries++ > f->hash_entries_max)
ospf_top_rehash(f, HASH_HI_STEP);
return e;
}
void
ospf_hash_delete(struct top_graph *f, struct top_hash_entry *e)
{
unsigned int h = ospf_top_hash(f, e->lsa.id, e->lsa.rt, e->lsa.type);
struct top_hash_entry **ee = f->hash_table + h;
while (*ee)
{
if (*ee == e)
{
*ee = e->next;
sl_free(f->hash_slab, e);
if (f->hash_entries-- < f->hash_entries_min)
ospf_top_rehash(f, -HASH_LO_STEP);
return;
}
ee = &((*ee)->next);
}
bug("ospf_hash_delete() called for invalid node");
}
void
ospf_top_dump(struct top_graph *f, struct proto *p)
{
unsigned int i;
OSPF_TRACE(D_EVENTS, "Hash entries: %d", f->hash_entries);
for(i=0; i<f->hash_size; i++)
{
struct top_hash_entry *e = f->hash_table[i];
while (e)
{
OSPF_TRACE(D_EVENTS, "\t%1x %-1I %-1I %4u 0x%08x",
e->lsa.type, e->lsa.id,
e->lsa.rt, e->lsa.age, e->lsa.sn);
e = e->next;
}
}
}
/* 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 ospf_area *oa)
{
struct ospf_iface *ifa;
struct ospf_neighbor *n;
struct proto_ospf *po=oa->po;
WALK_LIST(ifa, iface_list)
{
if(ifa->oa==oa)
{
WALK_LIST(n, ifa->neigh_list)
{
if((n->state==NEIGHBOR_EXCHANGE)||(n->state==NEIGHBOR_LOADING))
{
return 0;
}
}
}
}
return 1;
}