mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-23 10:11:53 +00:00
2187708520
Conflicts: configure.in lib/socket.h nest/protocol.h
1967 lines
46 KiB
C
1967 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 = po->lsab ? mb_realloc(po->lsab, po->lsab_size):
|
|
mb_alloc(po->proto.pool, 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;
|
|
u32 link_cost = po->stub_router ? 0xffff : ifa->cost;
|
|
|
|
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), link_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), link_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), link_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);
|