0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-10-18 18:08:45 +00:00
bird/proto/ospf/topology.c
2013-06-28 01:01:21 +02:00

1965 lines
46 KiB
C

/*
* BIRD -- OSPF Topological Database
*
* (c) 1999 Martin Mares <mj@ucw.cz>
* (c) 1999--2004 Ondrej Filip <feela@network.cz>
* (c) 2009--2012 Ondrej Zajicek <santiago@crfreenet.org>
* (c) 2009--2012 CZ.NIC z.s.p.o.
*
* 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
static inline u32
fibnode_to_lsaid(struct proto_ospf *po, struct fib_node *fn)
{
/* In OSPFv2, We have to map IP prefixes to u32 in such manner
that resulting u32 interpreted as IP address is a member of given
prefix. Therefore, /32 prefix have to be mapped on itself.
All received prefixes have to be mapped on different u32s.
We have an assumption that if there is nontrivial (non-/32)
network prefix, then there is not /32 prefix for the first
and the last IP address of the network (these are usually
reserved, therefore it is not an important restriction).
The network prefix is mapped to the first or the last
IP address in the manner that disallow collisions - we
use IP address that cannot be used by parent prefix.
For example:
192.168.0.0/24 maps to 192.168.0.255
192.168.1.0/24 maps to 192.168.1.0
because 192.168.0.0 and 192.168.1.255 might be used by
192.168.0.0/23 .
This is not compatible with older RFC 1583, so we have an option
to the RFC 1583 compatible assignment (that always uses the first
address) which disallows subnetting.
Appendig E of RFC 2328 suggests different algorithm, that tries
to maximize both compatibility and subnetting. But as it is not
possible to have both reliably and the suggested algorithm was
unnecessary complicated and it does crazy things like changing
LSA ID for a network because different network appeared, we
choose a different way.
In OSPFv3, it is simpler. There is not a requirement for
membership of the result in the input network, so we just use a
hash-based unique ID of a routing table entry for a route that
originated given LSA. For ext-LSA, it is an imported route in the
nest's routing table (p->table). For summary-LSA, it is a
'source' route in the protocol internal routing table (po->rtf).
*/
if (ospf_is_v3(po))
return fn->uid;
u32 id = ipa_to_u32(fn->prefix);
if ((po->rfc1583) || (fn->pxlen == 0) || (fn->pxlen == 32))
return id;
if (id & (1 << (32 - fn->pxlen)))
return id;
else
return id | ~u32_mkmask(fn->pxlen);
}
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_used);
memcpy(r, po->lsab, po->lsab_used);
po->lsab_used = 0;
return r;
}
static inline void *
lsab_offset(struct proto_ospf *po, unsigned offset)
{
return ((byte *) po->lsab) + offset;
}
static inline void *
lsab_end(struct proto_ospf *po)
{
return ((byte *) po->lsab) + po->lsab_used;
}
/*
* Router-LSA handling
* Type = LSA_T_RT
*/
static int
configured_stubnet(struct ospf_area *oa, struct ifa *a)
{
if (!oa->ac)
return 0;
/* Does not work for IA_PEER addresses, but it is not called on these */
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 int
bcast_net_active(struct ospf_iface *ifa)
{
struct ospf_neighbor *neigh;
if (ifa->state == OSPF_IS_WAITING)
return 0;
WALK_LIST(neigh, ifa->neigh_list)
{
if (neigh->state == NEIGHBOR_FULL)
{
if (neigh->rid == ifa->drid)
return 1;
if (ifa->state == OSPF_IS_DR)
return 1;
}
}
return 0;
}
static inline u32
get_rt_options(struct proto_ospf *po, struct ospf_area *oa, int bitv)
{
u32 opts = 0;
if (po->areano > 1)
opts |= OPT_RT_B;
if ((po->areano > 1) && oa_is_nssa(oa) && oa->ac->translator)
opts |= OPT_RT_NT;
if (po->ebit && !oa_is_stub(oa))
opts |= OPT_RT_E;
if (bitv)
opts |= OPT_RT_V;
return opts;
}
static inline void
add_rt2_lsa_link(struct proto_ospf *po, u8 type, u32 id, u32 data, u16 metric)
{
struct ospf_lsa_rt2_link *ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt2_link));
ln->type = type;
ln->id = id;
ln->data = data;
ln->metric = metric;
ln->no_tos = 0;
}
static void
prepare_rt2_lsa_body(struct proto_ospf *po, struct ospf_area *oa)
{
struct ospf_iface *ifa;
int i = 0, bitv = 0;
struct ospf_neighbor *neigh;
ASSERT(po->lsab_used == 0);
lsab_allocz(po, sizeof(struct ospf_lsa_rt));
/* ospf_lsa_rt header will be filled later */
WALK_LIST(ifa, po->iface_list)
{
int net_lsa = 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))
bitv = 1;
}
if ((ifa->oa != oa) || (ifa->state == OSPF_IS_DOWN))
continue;
ifa->rt_pos_beg = i;
/* RFC2328 - 12.4.1.1-4 */
switch (ifa->type)
{
case OSPF_IT_PTP:
case OSPF_IT_PTMP:
WALK_LIST(neigh, ifa->neigh_list)
if (neigh->state == NEIGHBOR_FULL)
{
/*
* ln->data should be ifa->iface_id in case of no/ptp
* address (ifa->addr->flags & IA_PEER) on PTP link (see
* RFC 2328 12.4.1.1.), but the iface ID value has no use,
* while using IP address even in this case is here for
* compatibility with some broken implementations that use
* this address as a next-hop.
*/
add_rt2_lsa_link(po, LSART_PTP, neigh->rid, ipa_to_u32(ifa->addr->ip), ifa->cost);
i++;
}
break;
case OSPF_IT_BCAST:
case OSPF_IT_NBMA:
if (bcast_net_active(ifa))
{
add_rt2_lsa_link(po, LSART_NET, ipa_to_u32(ifa->drip), ipa_to_u32(ifa->addr->ip), ifa->cost);
i++;
net_lsa = 1;
}
break;
case OSPF_IT_VLINK:
neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list);
if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff))
add_rt2_lsa_link(po, LSART_VLNK, neigh->rid, ipa_to_u32(ifa->addr->ip), ifa->cost), i++;
break;
default:
log("Unknown interface type %s", ifa->iface->name);
break;
}
ifa->rt_pos_end = i;
/* Now we will originate stub area if there is no primary */
if (net_lsa ||
(ifa->type == OSPF_IT_VLINK) ||
((ifa->addr->flags & IA_PEER) && ! ifa->cf->stub) ||
configured_stubnet(oa, ifa->addr))
continue;
/* Host or network stub entry */
if ((ifa->addr->flags & IA_HOST) ||
(ifa->state == OSPF_IS_LOOP) ||
(ifa->type == OSPF_IT_PTMP))
add_rt2_lsa_link(po, LSART_STUB, ipa_to_u32(ifa->addr->ip), 0xffffffff, 0);
else
add_rt2_lsa_link(po, LSART_STUB, ipa_to_u32(ifa->addr->prefix), u32_mkmask(ifa->addr->pxlen), ifa->cost);
i++;
ifa->rt_pos_end = i;
}
struct ospf_stubnet_config *sn;
if (oa->ac) // XXXX ???
WALK_LIST(sn, oa->ac->stubnet_list)
if (!sn->hidden)
add_rt2_lsa_link(po, LSART_STUB, ipa_to_u32(sn->px.addr), u32_mkmask(sn->px.len), sn->cost), i++;
struct ospf_lsa_rt *rt = po->lsab;
/* Store number of links in lower half of options */
rt->options = get_rt_options(po, oa, bitv) | (u16) i;
}
static inline void
add_rt3_lsa_link(struct proto_ospf *po, u8 type, struct ospf_iface *ifa, u32 nif, u32 id)
{
struct ospf_lsa_rt3_link *ln = lsab_alloc(po, sizeof(struct ospf_lsa_rt3_link));
ln->type = type;
ln->padding = 0;
ln->metric = ifa->cost;
ln->lif = ifa->iface_id;
ln->nif = nif;
ln->id = id;
}
static void
prepare_rt3_lsa_body(struct proto_ospf *po, struct ospf_area *oa)
{
struct ospf_iface *ifa;
struct ospf_neighbor *neigh;
int bitv = 0;
int i = 0;
ASSERT(po->lsab_used == 0);
lsab_allocz(po, sizeof(struct ospf_lsa_rt));
/* ospf_lsa_rt header will be filled later */
WALK_LIST(ifa, po->iface_list)
{
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))
bitv = 1;
}
if ((ifa->oa != oa) || (ifa->state == OSPF_IS_DOWN))
continue;
ifa->rt_pos_beg = i;
/* RFC5340 - 4.4.3.2 */
switch (ifa->type)
{
case OSPF_IT_PTP:
case OSPF_IT_PTMP:
WALK_LIST(neigh, ifa->neigh_list)
if (neigh->state == NEIGHBOR_FULL)
add_rt3_lsa_link(po, LSART_PTP, ifa, neigh->iface_id, neigh->rid), i++;
break;
case OSPF_IT_BCAST:
case OSPF_IT_NBMA:
if (bcast_net_active(ifa))
add_rt3_lsa_link(po, LSART_NET, ifa, ifa->dr_iface_id, ifa->drid), i++;
break;
case OSPF_IT_VLINK:
neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list);
if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff))
add_rt3_lsa_link(po, LSART_VLNK, ifa, neigh->iface_id, neigh->rid), i++;
break;
default:
log("Unknown interface type %s", ifa->iface->name);
break;
}
ifa->rt_pos_end = i;
}
struct ospf_lsa_rt *rt = po->lsab;
rt->options = get_rt_options(po, oa, bitv) | (oa->options & OPTIONS_MASK);
}
/**
* ospf_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
ospf_originate_rt_lsa(struct ospf_area *oa)
{
struct proto_ospf *po = oa->po;
struct ospf_lsa_new lsa = {
.type = LSA_T_RT,
.dom = oa->areaid,
.id = ospf_is_v2(po) ? po->router_id : 0,
.opts = oa->options
};
if (ospf_is_v2(po))
prepare_rt2_lsa_body(po, oa);
else
prepare_rt3_lsa_body(po, oa);
oa->rt = ospf_originate_lsa(po, &lsa);
}
/*
* Net-LSA handling
* Type = LSA_T_NET
*/
static void
prepare_net2_lsa_body(struct proto_ospf *po, struct ospf_iface *ifa)
{
struct ospf_lsa_net *net;
struct ospf_neighbor *n;
int nodes = ifa->fadj + 1;
u16 i = 1;
ASSERT(po->lsab_used == 0);
net = lsab_alloc(po, sizeof(struct ospf_lsa_net) + 4 * nodes);
net->optx = u32_mkmask(ifa->addr->pxlen);
net->routers[0] = po->router_id;
WALK_LIST(n, ifa->neigh_list)
{
if (n->state == NEIGHBOR_FULL)
{
net->routers[i] = n->rid;
i++;
}
}
ASSERT(i == nodes);
}
static void
prepare_net3_lsa_body(struct proto_ospf *po, struct ospf_iface *ifa)
{
struct ospf_lsa_net *net;
int nodes = ifa->fadj + 1;
u32 options = 0;
u16 i = 1;
ASSERT(po->lsab_used == 0);
net = lsab_alloc(po, sizeof(struct ospf_lsa_net) + 4 * nodes);
net->routers[0] = po->router_id;
struct ospf_neighbor *n;
WALK_LIST(n, ifa->neigh_list)
{
if (n->state == NEIGHBOR_FULL)
{
/* In OSPFv3, we would like to merge options from Link LSAs of added neighbors */
struct top_hash_entry *en =
ospf_hash_find(po->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK);
if (en)
options |= ((struct ospf_lsa_link *) en->lsa_body)->options;
net->routers[i] = n->rid;
i++;
}
}
ASSERT(i == nodes);
net->optx = options & OPTIONS_MASK;
}
/**
* ospf_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
ospf_originate_net_lsa(struct ospf_iface *ifa)
{
struct proto_ospf *po = ifa->oa->po;
struct ospf_lsa_new lsa = {
.type = LSA_T_NET,
.dom = ifa->oa->areaid,
.id = ospf_is_v2(po) ? ipa_to_u32(ifa->addr->ip) : ifa->iface_id,
.opts = ifa->oa->options,
.ifa = ifa
};
if (ospf_is_v2(po))
prepare_net2_lsa_body(po, ifa);
else
prepare_net3_lsa_body(po, ifa);
ifa->net_lsa = ospf_originate_lsa(po, &lsa);
}
/*
* (Net|Rt)-summary-LSA handling
* (a.k.a. Inter-Area-(Prefix|Router)-LSA)
* Type = LSA_T_SUM_NET, LSA_T_SUM_RT
*/
static inline void
prepare_sum2_lsa_body(struct proto_ospf *po, u32 mlen, u32 metric)
{
struct ospf_lsa_sum2 *sum;
sum = lsab_allocz(po, sizeof(struct ospf_lsa_sum2));
sum->netmask = ip4_mkmask(mlen);
sum->metric = metric;
}
static inline void
prepare_sum3_net_lsa_body(struct proto_ospf *po, struct fib_node *fn, u32 metric)
{
struct ospf_lsa_sum3_net *sum;
sum = lsab_allocz(po, sizeof(struct ospf_lsa_sum3_net) + IPV6_PREFIX_SPACE(fn->pxlen));
sum->metric = metric;
put_ipv6_prefix(sum->prefix, fn->prefix, fn->pxlen, 0, 0);
}
static inline void
prepare_sum3_rt_lsa_body(struct proto_ospf *po, u32 drid, u32 metric, u32 options)
{
struct ospf_lsa_sum3_rt *sum;
sum = lsab_allocz(po, sizeof(struct ospf_lsa_sum3_rt));
sum->options = options;
sum->metric = metric;
sum->drid = drid;
}
void
ospf_originate_sum_net_lsa(struct ospf_area *oa, struct fib_node *fn, int metric)
{
struct proto_ospf *po = oa->po;
struct ospf_lsa_new lsa = {
.type = LSA_T_SUM_NET,
.dom = oa->areaid,
.id = fibnode_to_lsaid(po, fn),
.opts = oa->options,
.fn = fn
};
if (ospf_is_v2(po))
prepare_sum2_lsa_body(po, fn->pxlen, metric);
else
prepare_sum3_net_lsa_body(po, fn, metric);
ospf_originate_lsa(po, &lsa);
}
void
ospf_originate_sum_rt_lsa(struct ospf_area *oa, struct fib_node *fn, int metric, u32 options)
{
struct proto_ospf *po = oa->po;
u32 rid = ipa_to_rid(fn->prefix);
/* In OSPFv3, LSA ID is meaningless, but we still use Router ID of ASBR */
struct ospf_lsa_new lsa = {
.type = LSA_T_SUM_RT,
.dom = oa->areaid,
.id = rid,
.opts = oa->options
};
if (ospf_is_v2(po))
prepare_sum2_lsa_body(po, 0, metric);
else
prepare_sum3_rt_lsa_body(po, rid, metric, options & OPTIONS_MASK);
ospf_originate_lsa(po, &lsa);
}
/*
* AS-external-LSA and NSSA-LSA handling
* Type = LSA_T_EXT, LSA_T_NSSA
*/
static inline void
prepare_ext2_lsa_body(struct proto_ospf *po, struct fib_node *fn,
u32 metric, ip_addr fwaddr, u32 tag)
{
struct ospf_lsa_ext2 *ext;
ext = lsab_allocz(po, sizeof(struct ospf_lsa_ext2));
ext->metric = metric;
ext->netmask = ip4_mkmask(fn->pxlen);
ext->fwaddr = ipa_to_ip4(fwaddr);
ext->tag = tag;
}
static inline void
prepare_ext3_lsa_body(struct proto_ospf *po, struct fib_node *fn,
u32 metric, ip_addr fwaddr, u32 tag, int pbit)
{
struct ospf_lsa_ext3 *ext;
int bsize = sizeof(struct ospf_lsa_ext3)
+ IPV6_PREFIX_SPACE(fn->pxlen)
+ (ipa_nonzero(fwaddr) ? 16 : 0)
+ (tag ? 4 : 0);
ext = lsab_allocz(po, bsize);
ext->metric = metric;
u32 *buf = ext->rest;
buf = put_ipv6_prefix(buf, fn->prefix, fn->pxlen, pbit ? OPT_PX_P : 0, 0);
if (ipa_nonzero(fwaddr))
{
ext->metric |= LSA_EXT3_FBIT;
buf = put_ipv6_addr(buf, fwaddr);
}
if (tag)
{
ext->metric |= LSA_EXT3_TBIT;
*buf++ = tag;
}
}
static void
prepare_ext_lsa_body(struct proto_ospf *po, struct fib_node *fn,
u32 metric, ip_addr fwaddr, u32 tag, int pbit)
{
if (ospf_is_v2(po))
prepare_ext2_lsa_body(po, fn, metric, fwaddr, tag);
else
prepare_ext3_lsa_body(po, fn, metric, fwaddr, tag, pbit);
}
/**
* originate_ext_lsa - new route received from nest and filters
* @oa: ospf_area for which LSA is originated
* @fn: network prefix and mask
* @src: the source of origination of the LSA (EXT_EXPORT/EXT_NSSA)
* @metric: the metric of a route (possibly with appropriate E-bit)
* @fwaddr: the forwarding address
* @tag: the route tag
* @pbit: P-bit for NSSA LSAs, ignored for external LSAs
*
* If I receive a message that new route is installed, I try to originate an
* external LSA. If @oa is an NSSA area, NSSA-LSA is originated instead.
* @oa should not be a stub area. @src does not specify whether the LSA
* is external or NSSA, but it specifies the source of origination -
* the export from ospf_rt_notify(), or the NSSA-EXT translation.
*
* The function also sets flag ebit. If it's the first time, the new router lsa
* origination is necessary.
*/
void
ospf_originate_ext_lsa(struct proto_ospf *po, struct fib_node *fn, int src,
u32 metric, ip_addr fwaddr, u32 tag)
{
struct ospf_lsa_new lsa = {
.type = LSA_T_EXT,
.dom = 0,
.id = fibnode_to_lsaid(po, fn),
.opts = OPT_E,
.fn = fn
};
prepare_ext_lsa_body(po, fn, metric, fwaddr, tag, 0);
// XXXX: review
if (src)
{
fn->flags &= ~OSPF_RT_SRC;
fn->flags |= src;
}
// XXXX: review
if (po->ebit == 0)
{
struct ospf_area *ai;
po->ebit = 1;
WALK_LIST(ai, po->area_list)
schedule_rt_lsa(ai);
}
ospf_originate_lsa(po, &lsa);
}
static inline ip_addr
find_surrogate_fwaddr(struct ospf_area *oa)
{
struct proto_ospf *po = oa->po;
struct ospf_iface *ifa;
struct ifa *a, *cur_addr = NULL;
int np, cur_np = 0;
WALK_LIST(ifa, po->iface_list)
{
if ((ifa->oa != oa) ||
(ifa->type == OSPF_IT_VLINK))
continue;
if (ospf_is_v2(po))
{
a = ifa->addr;
if (a->flags & IA_PEER)
continue;
np = ((a->flags & IA_HOST) || ifa->stub) ? 2 : 1;
if (np > cur_np)
{
cur_addr = a;
cur_np = np;
}
}
else /* OSPFv3 */
{
WALK_LIST(a, ifa->iface->addrs)
{
if ((a->flags & IA_SECONDARY) ||
(a->flags & IA_PEER) ||
(a->scope <= SCOPE_LINK))
continue;
np = ((a->flags & IA_HOST) || ifa->stub) ? 2 : 1;
if (np > cur_np)
{
cur_addr = a;
cur_np = np;
}
}
}
}
return cur_addr ? cur_addr->ip : IPA_NONE;
}
void
ospf_originate_nssa_lsa(struct ospf_area *oa, struct fib_node *fn, int src,
u32 metric, ip_addr fwaddr, u32 tag, int pbit)
{
struct proto_ospf *po = oa->po;
struct ospf_lsa_new lsa = {
.type = LSA_T_NSSA,
.dom = oa->areaid,
.id = fibnode_to_lsaid(po, fn),
.opts = pbit ? OPT_P : 0,
.fn = fn
};
/* NSSA-LSA with P-bit set must have non-zero forwarding address */
if (pbit && ipa_zero(fwaddr))
{
fwaddr = find_surrogate_fwaddr(oa);
if (ipa_zero(fwaddr))
{
log(L_ERR "%s: Cannot find forwarding address for NSSA-LSA %I/%d",
po->proto.name, fn->prefix, fn->pxlen);
return;
}
}
prepare_ext_lsa_body(po, fn, metric, fwaddr, tag, pbit);
// XXXX: review
if (src)
{
fn->flags &= ~OSPF_RT_SRC;
fn->flags |= src;
}
// XXXX: review
if (po->ebit == 0)
{
struct ospf_area *ai;
po->ebit = 1;
WALK_LIST(ai, po->area_list)
schedule_rt_lsa(ai);
}
ospf_originate_lsa(po, &lsa);
}
/*
* Link-LSA handling (assume OSPFv3)
* Type = LSA_T_LINK
*/
static inline void
lsab_put_prefix(struct proto_ospf *po, ip_addr prefix, u32 pxlen, u32 cost)
{
void *buf = lsab_alloc(po, IPV6_PREFIX_SPACE(pxlen));
u8 flags = (pxlen < MAX_PREFIX_LENGTH) ? 0 : OPT_PX_LA;
put_ipv6_prefix(buf, prefix, pxlen, flags, cost);
}
static void
prepare_link_lsa_body(struct proto_ospf *po, struct ospf_iface *ifa)
{
struct ospf_lsa_link *ll;
int i = 0;
ASSERT(po->lsab_used == 0);
ll = lsab_allocz(po, sizeof(struct ospf_lsa_link));
ll->options = ifa->oa->options | (ifa->priority << 24);
ll->lladdr = ifa->addr->ip;
ll = NULL; /* buffer might be reallocated later */
struct ifa *a;
WALK_LIST(a, ifa->iface->addrs)
{
if ((a->flags & IA_SECONDARY) ||
(a->scope < SCOPE_SITE))
continue;
// XXXX: review, really 'ip' and 'pxlen' ?
lsab_put_prefix(po, a->ip, a->pxlen, 0);
i++;
}
ll = po->lsab;
ll->pxcount = i;
}
void
ospf_originate_link_lsa(struct ospf_iface *ifa)
{
struct proto_ospf *po = ifa->oa->po;
/* FIXME check for vlink and skip that? */
struct ospf_lsa_new lsa = {
.type = LSA_T_LINK,
.dom = ifa->iface_id,
.id = ifa->iface_id,
.ifa = ifa
};
prepare_link_lsa_body(po, ifa);
ospf_originate_lsa(po, &lsa);
// XXXX: review
/* Just to be sure to not forget on our link LSA */
if (ifa->state == OSPF_IS_DR)
schedule_net_lsa(ifa);
}
/*
* Prefix-Rt-LSA handling (assume OSPFv3)
* Type = LSA_T_PREFIX, referred type = LSA_T_RT
*/
static void
prepare_prefix_rt_lsa_body(struct proto_ospf *po, struct ospf_area *oa)
{
struct ospf_config *cf = (struct ospf_config *) (po->proto.cf);
struct ospf_iface *ifa;
struct ospf_lsa_prefix *lp;
int host_addr = 0;
int net_lsa;
int i = 0;
ASSERT(po->lsab_used == 0);
lp = lsab_allocz(po, sizeof(struct ospf_lsa_prefix));
lp->ref_type = LSA_T_RT;
lp->ref_id = 0;
lp->ref_rt = po->router_id;
lp = NULL; /* buffer might be reallocated later */
WALK_LIST(ifa, po->iface_list)
{
if ((ifa->oa != oa) || (ifa->type == OSPF_IT_VLINK) || (ifa->state == OSPF_IS_DOWN))
continue;
ifa->px_pos_beg = i;
if ((ifa->type == OSPF_IT_BCAST) ||
(ifa->type == OSPF_IT_NBMA))
net_lsa = bcast_net_active(ifa);
else
net_lsa = 0;
struct ifa *a;
WALK_LIST(a, ifa->iface->addrs)
{
if ((a->flags & IA_SECONDARY) ||
(a->flags & IA_PEER) ||
(a->scope <= SCOPE_LINK))
continue;
if (((a->pxlen < MAX_PREFIX_LENGTH) && net_lsa) ||
configured_stubnet(oa, a))
continue;
if ((a->flags & IA_HOST) ||
(ifa->state == OSPF_IS_LOOP) ||
(ifa->type == OSPF_IT_PTMP))
{
lsab_put_prefix(po, a->ip, MAX_PREFIX_LENGTH, 0);
host_addr = 1;
}
else
lsab_put_prefix(po, a->prefix, a->pxlen, ifa->cost);
i++;
}
ifa->px_pos_end = i;
}
struct ospf_stubnet_config *sn;
if (oa->ac)
WALK_LIST(sn, oa->ac->stubnet_list)
if (!sn->hidden)
{
lsab_put_prefix(po, sn->px.addr, sn->px.len, sn->cost);
if (sn->px.len == MAX_PREFIX_LENGTH)
host_addr = 1;
i++;
}
/* If there are some configured vlinks, find some global address
(even from another area), which will be used as a vlink endpoint. */
if (!EMPTY_LIST(cf->vlink_list) && !host_addr)
{
WALK_LIST(ifa, po->iface_list)
{
if ((ifa->type == OSPF_IT_VLINK) || (ifa->state == OSPF_IS_DOWN))
continue;
struct ifa *a;
WALK_LIST(a, ifa->iface->addrs)
{
if ((a->flags & IA_SECONDARY) || (a->scope <= SCOPE_LINK))
continue;
/* Found some IP */
lsab_put_prefix(po, a->ip, MAX_PREFIX_LENGTH, 0);
i++;
goto done;
}
}
}
done:
lp = po->lsab;
lp->pxcount = i;
}
void
ospf_originate_prefix_rt_lsa(struct ospf_area *oa)
{
struct proto_ospf *po = oa->po;
struct ospf_lsa_new lsa = {
.type = LSA_T_PREFIX,
.dom = oa->areaid,
.id = 0
};
prepare_prefix_rt_lsa_body(po, oa);
ospf_originate_lsa(po, &lsa);
}
/*
* Prefix-Net-LSA handling (assume OSPFv3)
* Type = LSA_T_PREFIX, referred type = LSA_T_NET
*/
static inline int
prefix_space(u32 *buf)
{
int pxl = *buf >> 24;
return IPV6_PREFIX_SPACE(pxl);
}
static inline int
prefix_same(u32 *b1, u32 *b2)
{
int pxl1 = *b1 >> 24;
int pxl2 = *b2 >> 24;
int pxs, i;
if (pxl1 != pxl2)
return 0;
pxs = IPV6_PREFIX_WORDS(pxl1);
for (i = 1; i < pxs; i++)
if (b1[i] != b2[i])
return 0;
return 1;
}
static inline u32 *
prefix_advance(u32 *buf)
{
int pxl = *buf >> 24;
return buf + IPV6_PREFIX_WORDS(pxl);
}
/* FIXME eliminate items with LA bit set? see 4.4.3.9 */
static void
add_prefix(struct proto_ospf *po, u32 *px, int offset, int *pxc)
{
u32 *pxl = lsab_offset(po, offset);
int i;
for (i = 0; i < *pxc; pxl = prefix_advance(pxl), i++)
if (prefix_same(px, pxl))
{
/* Options should be logically OR'ed together */
*pxl |= (*px & 0x00FF0000);
return;
}
ASSERT(pxl == lsab_end(po));
int pxspace = prefix_space(px);
pxl = lsab_alloc(po, pxspace);
memcpy(pxl, px, pxspace);
*pxl &= 0xFFFF0000; /* Set metric to zero */
(*pxc)++;
}
static void
add_link_lsa(struct proto_ospf *po, struct top_hash_entry *en, int offset, int *pxc)
{
struct ospf_lsa_link *ll = en->lsa_body;
u32 *pxb = ll->rest;
int j;
for (j = 0; j < ll->pxcount; pxb = prefix_advance(pxb), j++)
{
u8 pxlen = (pxb[0] >> 24);
u8 pxopts = (pxb[0] >> 16);
/* Skip NU or LA prefixes */
if (pxopts & (OPT_PX_NU | OPT_PX_LA))
continue;
/* Skip link-local prefixes */
if ((pxlen >= 10) && ((pxb[1] & 0xffc00000) == 0xfe800000))
continue;
add_prefix(po, pxb, offset, pxc);
}
}
static void
prepare_prefix_net_lsa_body(struct proto_ospf *po, struct ospf_iface *ifa)
{
struct ospf_lsa_prefix *lp;
struct ospf_neighbor *n;
struct top_hash_entry *en;
int pxc, offset;
ASSERT(po->lsab_used == 0);
lp = lsab_allocz(po, sizeof(struct ospf_lsa_prefix));
lp->ref_type = LSA_T_NET;
lp->ref_id = ifa->net_lsa->lsa.id;
lp->ref_rt = po->router_id;
lp = NULL; /* buffer might be reallocated later */
pxc = 0;
offset = po->lsab_used;
/* Find all Link LSAs associated with the link and merge their prefixes */
if (ifa->link_lsa)
add_link_lsa(po, ifa->link_lsa, offset, &pxc);
WALK_LIST(n, ifa->neigh_list)
if ((n->state == NEIGHBOR_FULL) &&
(en = ospf_hash_find(po->gr, ifa->iface_id, n->iface_id, n->rid, LSA_T_LINK)))
add_link_lsa(po, en, offset, &pxc);
lp = po->lsab;
lp->pxcount = pxc;
}
void
ospf_originate_prefix_net_lsa(struct ospf_iface *ifa)
{
struct proto_ospf *po = ifa->oa->po;
struct ospf_lsa_new lsa = {
.type = LSA_T_PREFIX,
.dom = ifa->oa->areaid,
.id = ifa->iface_id,
.ifa = ifa
};
prepare_prefix_net_lsa_body(po, ifa);
ifa->pxn_lsa = ospf_originate_lsa(po, &lsa);
}
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 unsigned
ospf_top_hash(struct top_graph *f, u32 domain, u32 lsaid, u32 rtrid, u32 type)
{
/* In OSPFv2, we don't know Router ID when looking for network LSAs.
In OSPFv3, we don't know LSA ID when looking for router LSAs.
In both cases, there is (usually) just one (or small number)
appropriate LSA, so we just clear unknown part of key. */
return (((f->ospf2 && (type == LSA_T_NET)) ? 0 : ospf_top_hash_u32(rtrid)) +
((!f->ospf2 && (type == LSA_T_RT)) ? 0 : ospf_top_hash_u32(lsaid)) +
type + domain) & f->hash_mask;
/*
return (ospf_top_hash_u32(lsaid) + ospf_top_hash_u32(rtrid) +
type + areaid) & f->hash_mask;
*/
}
/**
* 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(pool *pool)
{
struct top_graph *f;
f = mb_allocz(pool, sizeof(struct top_graph));
f->pool = 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->domain, 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(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type)
{
struct top_hash_entry *e;
e = f->hash_table[ospf_top_hash(f, domain, lsa, rtr, type)];
while (e && (e->lsa.id != lsa || e->lsa.rt != rtr ||
e->lsa_type != type || e->domain != domain))
e = e->next;
return e;
}
/* In OSPFv2, lsa.id is the same as lsa.rt for router LSA. In OSPFv3, we don't know
lsa.id when looking for router LSAs. We return matching LSA with smallest lsa.id. */
struct top_hash_entry *
ospf_hash_find_rt(struct top_graph *f, u32 domain, u32 rtr)
{
struct top_hash_entry *rv = NULL;
struct top_hash_entry *e;
/* We can put rtr for lsa.id to hash fn, it is ignored in OSPFv3 */
e = f->hash_table[ospf_top_hash(f, domain, rtr, rtr, LSA_T_RT)];
while (e)
{
if (e->lsa.rt == rtr && e->lsa_type == LSA_T_RT && e->domain == domain)
{
if (f->ospf2 && (e->lsa.id == rtr))
return e;
if (!f->ospf2 && (!rv || e->lsa.id < rv->lsa.id))
rv = e;
}
e = e->next;
}
return rv;
}
static inline struct top_hash_entry *
find_matching_rt3(struct top_hash_entry *e, u32 domain, u32 rtr)
{
while (e && (e->lsa.rt != rtr || e->lsa_type != LSA_T_RT || e->domain != domain))
e = e->next;
return e;
}
struct top_hash_entry *
ospf_hash_find_rt3_first(struct top_graph *f, u32 domain, u32 rtr)
{
struct top_hash_entry *e;
e = f->hash_table[ospf_top_hash(f, domain, 0, rtr, LSA_T_RT)];
return find_matching_rt3(e, domain, rtr);
}
struct top_hash_entry *
ospf_hash_find_rt3_next(struct top_hash_entry *e)
{
return find_matching_rt3(e->next, e->domain, e->lsa.rt);
}
/* In OSPFv2, we don't know Router ID when looking for network LSAs.
There should be just one, so we find any match. */
struct top_hash_entry *
ospf_hash_find_net2(struct top_graph *f, u32 domain, u32 id)
{
struct top_hash_entry *e;
e = f->hash_table[ospf_top_hash(f, domain, id, 0, LSA_T_NET)];
while (e && (e->lsa.id != id || e->lsa_type != LSA_T_NET || e->domain != domain))
e = e->next;
return e;
}
struct top_hash_entry *
ospf_hash_get(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type)
{
struct top_hash_entry **ee;
struct top_hash_entry *e;
ee = f->hash_table + ospf_top_hash(f, domain, lsa, rtr, type);
e = *ee;
while (e && (e->lsa.id != lsa || e->lsa.rt != rtr ||
e->lsa_type != type || e->domain != domain))
e = e->next;
if (e)
return e;
e = sl_alloc(f->hash_slab);
e->color = OUTSPF;
e->dist = LSINFINITY;
e->nhs = NULL;
e->lb = IPA_NONE;
e->lsa.id = lsa;
e->lsa.rt = rtr;
e->lsa.type = type;
e->lsa_body = NULL;
e->lsa_type = type;
e->domain = domain;
e->next = *ee;
*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)
{
struct top_hash_entry **ee = f->hash_table +
ospf_top_hash(f, e->domain, e->lsa.id, e->lsa.rt, e->lsa_type);
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");
}
/*
static void
ospf_dump_lsa(struct top_hash_entry *he, struct proto *p)
{
struct ospf_lsa_rt *rt = NULL;
struct ospf_lsa_rt_link *rr = NULL;
struct ospf_lsa_net *ln = NULL;
u32 *rts = NULL;
u32 i, max;
OSPF_TRACE(D_EVENTS, "- %1x %-1R %-1R %4u 0x%08x 0x%04x %-1R",
he->lsa.type, he->lsa.id, he->lsa.rt, he->lsa.age, he->lsa.sn,
he->lsa.checksum, he->domain);
switch (he->lsa.type)
{
case LSA_T_RT:
rt = he->lsa_body;
rr = (struct ospf_lsa_rt_link *) (rt + 1);
for (i = 0; i < lsa_rt_items(&he->lsa); i++)
OSPF_TRACE(D_EVENTS, " - %1x %-1R %-1R %5u",
rr[i].type, rr[i].id, rr[i].data, rr[i].metric);
break;
case LSA_T_NET:
ln = he->lsa_body;
rts = (u32 *) (ln + 1);
for (i = 0; i < lsa_net_items(&he->lsa); i++)
OSPF_TRACE(D_EVENTS, " - %-1R", rts[i]);
break;
default:
break;
}
}
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;
for (e = f->hash_table[i]; e != NULL; e = e->next)
ospf_dump_lsa(e, p);
}
}
*/
/* 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 proto_ospf *po)
{
struct ospf_iface *ifa;
struct ospf_neighbor *n;
WALK_LIST(ifa, po->iface_list)
{
WALK_LIST(n, ifa->neigh_list)
{
if ((n->state == NEIGHBOR_EXCHANGE) || (n->state == NEIGHBOR_LOADING))
return 0;
break;
}
}
return 1;
}
/**
* ospf_install_lsa - install new LSA into database
* @po: OSPF protocol
* @lsa: LSA header
* @domain: domain of LSA
* @body: pointer to LSA body
*
* This function ensures installing new LSA into LSA database. Old instance is
* replaced. Several actions are taken to detect if new routing table
* calculation is necessary. This is described in 13.2 of RFC 2328.
*/
struct top_hash_entry *
ospf_install_lsa(struct proto_ospf *po, struct ospf_lsa_header *lsa, u32 domain, void *body)
{
/* LSA can be temporary, but body must be mb_allocated. */
int change = 0;
struct top_hash_entry *en;
en = ospf_hash_get(po->gr, domain, lsa->id, lsa->rt, lsa_type);
if ((en = ospf_hash_find_header(po->gr, domain, lsa)) == NULL)
{
en = ospf_hash_get_header(po->gr, domain, lsa);
change = 1;
}
else
{
if ((en->lsa.length != lsa->length) ||
(en->lsa.type_raw != lsa->type_raw) || /* check for OSPFv2 options */
(en->lsa.age == LSA_MAXAGE) ||
(lsa->age == LSA_MAXAGE) ||
memcmp(en->lsa_body, body, lsa->length - sizeof(struct ospf_lsa_header)))
change = 1;
s_rem_node(SNODE en);
}
DBG("Inst lsa: Id: %R, Rt: %R, Type: %u, Age: %u, Sum: %u, Sn: 0x%x\n",
lsa->id, lsa->rt, lsa->type, lsa->age, lsa->checksum, lsa->sn);
s_add_tail(&po->lsal, SNODE en);
en->inst_t = now;
if (en->lsa_body != NULL)
mb_free(en->lsa_body);
en->lsa_body = body;
memcpy(&en->lsa, lsa, sizeof(struct ospf_lsa_header));
en->ini_age = en->lsa.age;
if (change)
schedule_rtcalc(po);
return en;
}
struct top_hash_entry *
ospf_originate_lsa(struct proto_ospf *po, struct ospf_lsa_new *lsa)
{
struct top_hash_entry *en;
u32 lsa_rt = po->router_id;
u16 lsa_blen = po->lsab_used;
u16 lsa_length = sizeof(struct ospf_lsa_header) + lsa_len1;
void *lsa_body = po->lsab;
en = ospf_hash_get(po->gr, lsa->dom, lsa->id, lsa_rt, lsa->type);
if (en->lsa_next_body)
{
/* The scheduled LSA is the same as the new one */
if ((lsa_blen == en->lsa_next_blen) && !memcmp(lsa_body, en->lsa_next_body, lsa_blen))
goto drop;
// XXXX logmessage
/* Replace the scheduled LSA by the new one */
mb_free(en->lsa_next_body);
goto schedule;
}
if (en->lsa_body && (en->lsa.age != LSA_MAXAGE))
{
/*
casy:
aktualni valid a stejne -> vyskocit
aktualni valid, ale koliduje LSA ID -> error
plati MINLSINTERVAL -> odlozit
aktualni MAXSEQNUM -> odlozit
*/
aa;
}
if (en->lsa_body && (en->lsa.sn == LSA_MAXSEQNO))
{
if (en->lsa.age != LSA_MAXAGE)
{
}
goto schedule;
}
if (en->inst_t && ((en->inst_t + MINLSINTERVAL) > now))
{
// XXXX logmessage
goto schedule;
}
if (ospf_is_v2(po))
lsa_set_options(&hdr, options);
s_add_tail(&po->lsal, SNODE en);
en->inst_t = now;
if (en->lsa_body != NULL)
mb_free(en->lsa_body);
en->lsa_body = lsab_flush(po);
en->ini_age = en->lsa.age;
lsasum_calculate(&hdr, body);
ospf_lsupd_flood(po, en, NULL);
return en;
schedule:
en->lsa_next_body = lsab_flush(po);
en->lsa_next_blen = blen;
return en;
drop:
lsab_reset(po);
return en;
}
void
ospf_flush_lsa(struct proto_ospf *po, struct top_hash_entry *en)
{
OSPF_TRACE(D_EVENTS, "Flushing LSA: Type: %04x, Id: %R, Rt: %R",
en->lsa_type, en->lsa.id, en->lsa.rt);
en->lsa.age = LSA_MAXAGE;
ospf_lsupd_flood(po, en, NULL);
}
void
ospf_remove_lsa(struct proto_ospf *po, struct top_hash_entry *en)
{
// XXXX: more cleanup
s_rem_node(SNODE en);
mb_free(en->lsa_body);
en->lsa_body = NULL;
// ospf_hash_delete(po->gr, en);
}
static void
ospf_refresh_lsa(struct proto_ospf *po, struct top_hash_entry *en)
{
OSPF_TRACE(D_EVENTS, "Refreshing LSA: Type: %u, Id: %R, Rt: %R",
en->lsa_type, en->lsa.id, en->lsa.rt);
en->lsa.sn++;
en->lsa.age = 0;
en->ini_age = 0;
en->inst_t = now;
lsasum_calculate(&en->lsa, en->lsa_body);
ospf_lsupd_flood(po, en, NULL);
}
/* KILL */
void
update_rt_lsa(struct ospf_area *oa)
{
struct proto_ospf *po = oa->po;
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.
*/
originate_rt_lsa(oa);
if (ospf_is_v3(po))
originate_prefix_rt_lsa(oa);
schedule_rtcalc(po);
oa->origrt = 0;
}
void
update_net_lsa(struct ospf_iface *ifa)
{
struct proto_ospf *po = ifa->oa->po;
if (ifa->net_lsa && ((ifa->net_lsa->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))
{
flush_net_lsa(ifa);
if (ospf_is_v3(po))
flush_prefix_net_lsa(ifa);
}
else
{
originate_net_lsa(ifa);
if (ospf_is_v3(po))
originate_prefix_net_lsa(ifa);
}
schedule_rtcalc(po);
ifa->orignet = 0;
}
void
flush_net_lsa(struct ospf_iface *ifa)
{
struct proto_ospf *po = ifa->oa->po;
u32 dom = ifa->oa->areaid;
if (ifa->net_lsa == NULL)
return;
OSPF_TRACE(D_EVENTS, "Flushing network-LSA for iface %s",
ifa->iface->name);
ifa->net_lsa->lsa.sn += 1;
ifa->net_lsa->lsa.age = LSA_MAXAGE;
lsasum_calculate(&ifa->net_lsa->lsa, ifa->net_lsa->lsa_body);
ospf_lsupd_flood(po, NULL, NULL, &ifa->net_lsa->lsa, dom, 0);
flush_lsa(ifa->net_lsa, po);
ifa->net_lsa = NULL;
}
static inline int
check_sum2_net_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric)
{
struct ospf_lsa_sum2 *sum = en->lsa_body;
/* LSAID collision */
if (fn->pxlen != ip4_masklen(sum->netmask))
return -1;
return (en->lsa.sn != LSA_MAXSEQNO) && (sum->metric == metric);
}
static inline int
check_sum3_net_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric)
{
struct ospf_lsa_sum3_net *sum = en->lsa_body;
ip6_addr prefix;
int pxlen;
u8 pxopts;
u16 rest;
lsa_get_ipv6_prefix(sum->prefix, &prefix, &pxlen, &pxopts, &rest);
/* LSAID collision */
if ((fn->pxlen != pxlen) || !ip6_equal(fn->prefix, prefix))
return -1;
return (en->lsa.sn != LSA_MAXSEQNO) && (sum->metric == metric);
}
static int
check_sum_net_lsa(struct proto_ospf *po, struct top_hash_entry *en, struct fib_node *fn, u32 metric)
{
int rv = ospf_is_v2(po) ?
check_sum2_net_lsa(en, fn, metric) :
check_sum3_net_lsa(en, fn, metric);
if (rv < 0)
log(L_ERR "%s: LSAID collision for %I/%d", po->proto.name, fn->prefix, fn->pxlen);
return rv;
}
static int
check_sum_rt_lsa(struct proto_ospf *po, struct top_hash_entry *en, u32 drid, u32 metric, u32 options)
{
if (en->lsa.sn == LSA_MAXSEQNO)
return 0;
if (ospf_is_v2(po))
{
struct ospf_lsa_sum2 *sum = en->lsa_body;
return (sum->metric == metric);
}
else
{
struct ospf_lsa_sum3_rt *sum = en->lsa_body;
return (sum->options == options) && (sum->metric == metric) && (sum->drid == drid);
}
}
void
flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type)
{
struct proto_ospf *po = oa->po;
struct top_hash_entry *en;
u32 dom, lsid, type;
if (type == ORT_NET)
{
dom = oa->areaid;
type = LSA_T_SUM_NET;
lsid = fibnode_to_lsaid(po, fn);
}
else
{
/* In OSPFv3, LSA ID is meaningless, but we still use Router ID of ASBR */
dom = oa->areaid;
type = LSA_T_SUM_RT;
lsid = ipa_to_rid(fn->prefix);
}
en = ospf_hash_find(po->gr, dom, lsid, po->router_id, type);
if (en)
{
OSPF_TRACE(D_EVENTS, "Flushing summary-LSA (id=%R, type=%x)", lsid, type);
if ((type == ORT_NET) && (check_sum_net_lsa(po, en, fn, 0) < 0))
return;
struct ospf_lsa_sum *sum = en->lsa_body;
en->lsa.age = LSA_MAXAGE;
en->lsa.sn = LSA_MAXSEQNO;
lsasum_calculate(&en->lsa, sum);
ospf_lsupd_flood(po, NULL, NULL, &en->lsa, dom, 1);
if (can_flush_lsa(po)) flush_lsa(en, po);
}
}
/*
* check_ext_lsa() combines functions of check_*_lsaid_collision() and
* check_*_lsa_same(). 'en' is existing ext LSA, and rest parameters
* are parameters of new ext route. Function returns -1 if there is
* LSAID collision, returns 1 if the existing LSA is the same and
* returns 0 otherwise (in that case, we need to originate a new LSA).
*
* Really, checking for the same parameters is not as important as in
* summary LSA origination, because in most cases the duplicate
* external route propagation would be stopped by the nest. But there
* are still some cases (route reload, the same route propagated through
* different protocol) so it is also done here.
*/
static inline int
check_ext2_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric, ip_addr fwaddr, u32 tag)
{
struct ospf_lsa_ext2 *ext = en->lsa_body;
/* LSAID collision */
if (fn->pxlen != ip4_masklen(ext->netmask))
return -1;
return (en->lsa.sn != LSA_MAXSEQNO) && (ext->metric == metric) &&
(ext->tag == tag) && ip4_equal(ext->fwaddr, ipa_to_ip4(fwaddr));
}
static inline int
check_ext3_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric, ip_addr fwaddr, u32 tag)
{
struct ospf_lsa_ext3 *ext = en->lsa_body;
ip_addr prefix;
int pxlen;
u8 pxopts;
u16 rest;
u32 *buf = lsa_get_ipv6_prefix(ext->rest, &prefix, &pxlen, &pxopts, &rest);
/* LSAID collision */
if ((fn->pxlen != pxlen) || !ipa_equal(fn->prefix, prefix))
return -1;
if (en->lsa.sn == LSA_MAXSEQNO)
return 0;
u32 rt_metric = ext->metric & METRIC_MASK;
ip_addr rt_fwaddr = IPA_NONE;
u32 rt_tag = 0;
if (ext->metric & LSA_EXT3_FBIT)
buf = lsa_get_ipv6_addr(buf, &rt_fwaddr);
if (ext->metric & LSA_EXT3_TBIT)
rt_tag = *buf++;
return (rt_metric == metric) && ipa_equal(rt_fwaddr, fwaddr) && (rt_tag == tag);
}
static int
check_ext_lsa(struct proto_ospf *po, struct top_hash_entry *en, struct fib_node *fn,
u32 metric, ip_addr fwaddr, u32 tag)
{
int rv = ospf_is_v2(po) ?
check_ext2_lsa(en, fn, metric, fwaddr, tag) :
check_ext3_lsa(en, fn, metric, fwaddr, tag);
if (rv < 0)
log(L_ERR "%s: LSAID collision for %I/%d", po->proto.name, fn->prefix, fn->pxlen);
return rv;
}
void
flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int src, int nssa)
{
struct proto_ospf *po = oa->po;
struct proto *p = &po->proto;
struct top_hash_entry *en;
u32 dom = nssa ? oa->areaid : 0;
u32 type = nssa ? LSA_T_NSSA : LSA_T_EXT;
u32 lsid = fibnode_to_lsaid(po, fn);
en = ospf_hash_find(po->gr, dom, lsid, po->router_id, type);
if (en)
{
OSPF_TRACE(D_EVENTS, "Flushing %s-LSA for %I/%d",
nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen);
if (check_ext_lsa(po, en, fn, 0, IPA_NONE, 0) < 0)
return;
/* Clean up source bits */
if (src) // XXXX ???
fn->flags &= ~OSPF_RT_SRC;
ospf_lsupd_flush_nlsa(po, en);
}
}
void
update_link_lsa(struct ospf_iface *ifa)
{
if (ifa->link_lsa && ((ifa->link_lsa->inst_t + MINLSINTERVAL) > now))
return;
/*
* It's too early to originate new link LSA. We will
* try to do it next tick
*/
originate_link_lsa(ifa);
ifa->origlink = 0;
}
void
flush_prefix_net_lsa(struct ospf_iface *ifa)
{
struct proto_ospf *po = ifa->oa->po;
struct top_hash_entry *en = ifa->pxn_lsa;
u32 dom = ifa->oa->areaid;
if (en == NULL)
return;
OSPF_TRACE(D_EVENTS, "Flushing network prefix-LSA for iface %s",
ifa->iface->name);
en->lsa.sn += 1;
en->lsa.age = LSA_MAXAGE;
lsasum_calculate(&en->lsa, en->lsa_body);
ospf_lsupd_flood(po, NULL, NULL, &en->lsa, dom, 0);
flush_lsa(en, po);
ifa->pxn_lsa = NULL;
}
static s32
get_seqnum(struct top_hash_entry *en)
{
if (!en)
return LSA_INITSEQNO;
if (en->lsa.sn == LSA_MAXSEQNO)
{
log(L_WARN "OSPF: Premature origination of LSA (Type: %04x, Id: %R, Rt: %R)",
en->lsa_type, en->lsa.id, en->lsa.rt);
return LSA_INITSEQNO;
}
return en->lsa.sn + 1;
}
OSPF_TRACE(D_EVENTS, "Originating router-LSA for area %R", oa->areaid);
OSPF_TRACE(D_EVENTS, "Originating network-LSA for iface %s", ifa->iface->name);
OSPF_TRACE(D_EVENTS, "Originating net-summary-LSA for %I/%d (metric %d)", fn->prefix, fn->pxlen, metric);
OSPF_TRACE(D_EVENTS, "Originating rt-summary-LSA for %R (metric %d)", rid, metric);
OSPF_TRACE(D_EVENTS, "Originating %s-LSA for %I/%d",
nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen);
OSPF_TRACE(D_EVENTS, "Originating link-LSA for iface %s", ifa->iface->name);
OSPF_TRACE(D_EVENTS, "Originating router prefix-LSA for area %R", oa->areaid);
OSPF_TRACE(D_EVENTS, "Originating network prefix-LSA for iface %s", ifa->iface->name);
en = ospf_hash_find(po->gr, lsa.dom, lsa.id, po->router_id, lsa.type);
if (en && check_ext_lsa(po, en, fn, metric, fwaddr, tag))
return;
*length = sizeof(struct ospf_lsa_header) + po->lsab_used;
return lsab_flush(po);
*length = po->lsab_used + sizeof(struct ospf_lsa_header);
return lsab_flush(po);