641 lines
15 KiB
C
641 lines
15 KiB
C
/* $OpenBSD: lsupdate.c,v 1.52 2023/06/20 15:19:55 claudio Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
|
|
* Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <siphash.h>
|
|
|
|
#include "ospf.h"
|
|
#include "ospfd.h"
|
|
#include "log.h"
|
|
#include "ospfe.h"
|
|
#include "rde.h"
|
|
|
|
struct ibuf *prepare_ls_update(struct iface *);
|
|
int add_ls_update(struct ibuf *, struct iface *, void *, u_int16_t,
|
|
u_int16_t);
|
|
int send_ls_update(struct ibuf *, struct iface *, struct in_addr, u_int32_t);
|
|
|
|
void ls_retrans_list_insert(struct nbr *, struct lsa_entry *);
|
|
void ls_retrans_list_remove(struct nbr *, struct lsa_entry *);
|
|
|
|
/* link state update packet handling */
|
|
int
|
|
lsa_flood(struct iface *iface, struct nbr *originator, struct lsa_hdr *lsa_hdr,
|
|
void *data)
|
|
{
|
|
struct nbr *nbr;
|
|
struct lsa_entry *le = NULL;
|
|
int queued = 0, dont_ack = 0;
|
|
int r;
|
|
|
|
LIST_FOREACH(nbr, &iface->nbr_list, entry) {
|
|
if (nbr == iface->self)
|
|
continue;
|
|
if (!(nbr->state & NBR_STA_FLOOD))
|
|
continue;
|
|
|
|
if (iface->state & IF_STA_DROTHER && !queued)
|
|
while ((le = ls_retrans_list_get(iface->self, lsa_hdr)))
|
|
ls_retrans_list_free(iface->self, le);
|
|
|
|
while ((le = ls_retrans_list_get(nbr, lsa_hdr)))
|
|
ls_retrans_list_free(nbr, le);
|
|
|
|
if (!(nbr->state & NBR_STA_FULL) &&
|
|
(le = ls_req_list_get(nbr, lsa_hdr)) != NULL) {
|
|
r = lsa_newer(lsa_hdr, le->le_lsa);
|
|
if (r > 0) {
|
|
/* to flood LSA is newer than requested */
|
|
ls_req_list_free(nbr, le);
|
|
/* new needs to be flooded */
|
|
} else if (r < 0) {
|
|
/* to flood LSA is older than requested */
|
|
continue;
|
|
} else {
|
|
/* LSA are equal */
|
|
ls_req_list_free(nbr, le);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (nbr == originator) {
|
|
dont_ack++;
|
|
continue;
|
|
}
|
|
|
|
/* non DR or BDR router keep all lsa in one retrans list */
|
|
if (iface->state & IF_STA_DROTHER) {
|
|
if (!queued)
|
|
ls_retrans_list_add(iface->self, data,
|
|
iface->rxmt_interval, 0);
|
|
queued = 1;
|
|
} else {
|
|
ls_retrans_list_add(nbr, data, iface->rxmt_interval, 0);
|
|
queued = 1;
|
|
}
|
|
}
|
|
|
|
if (!queued)
|
|
return (0);
|
|
|
|
if (iface == originator->iface && iface->self != originator) {
|
|
if (iface->dr == originator || iface->bdr == originator)
|
|
return (0);
|
|
if (iface->state & IF_STA_BACKUP)
|
|
return (0);
|
|
dont_ack++;
|
|
}
|
|
|
|
/*
|
|
* initial flood needs to be queued separately, timeout is zero
|
|
* and oneshot has to be set because the retransimssion queues
|
|
* are already loaded.
|
|
*/
|
|
switch (iface->type) {
|
|
case IF_TYPE_POINTOPOINT:
|
|
case IF_TYPE_BROADCAST:
|
|
ls_retrans_list_add(iface->self, data, 0, 1);
|
|
break;
|
|
case IF_TYPE_NBMA:
|
|
case IF_TYPE_POINTOMULTIPOINT:
|
|
case IF_TYPE_VIRTUALLINK:
|
|
LIST_FOREACH(nbr, &iface->nbr_list, entry) {
|
|
if (nbr == iface->self)
|
|
continue;
|
|
if (!(nbr->state & NBR_STA_FLOOD))
|
|
continue;
|
|
if (!TAILQ_EMPTY(&nbr->ls_retrans_list)) {
|
|
le = TAILQ_LAST(&nbr->ls_retrans_list,
|
|
lsa_head);
|
|
if (lsa_hdr->type != le->le_lsa->type ||
|
|
lsa_hdr->ls_id != le->le_lsa->ls_id ||
|
|
lsa_hdr->adv_rtr != le->le_lsa->adv_rtr)
|
|
continue;
|
|
}
|
|
ls_retrans_list_add(nbr, data, 0, 1);
|
|
}
|
|
break;
|
|
default:
|
|
fatalx("lsa_flood: unknown interface type");
|
|
}
|
|
|
|
return (dont_ack == 2);
|
|
}
|
|
|
|
struct ibuf *
|
|
prepare_ls_update(struct iface *iface)
|
|
{
|
|
struct ibuf *buf;
|
|
|
|
if ((buf = ibuf_dynamic(iface->mtu - sizeof(struct ip),
|
|
IP_MAXPACKET - sizeof(struct ip))) == NULL)
|
|
fatal("prepare_ls_update");
|
|
|
|
/* OSPF header */
|
|
if (gen_ospf_hdr(buf, iface, PACKET_TYPE_LS_UPDATE))
|
|
goto fail;
|
|
|
|
/* reserve space for number of lsa field */
|
|
if (ibuf_add_zero(buf, sizeof(u_int32_t)) == -1)
|
|
goto fail;
|
|
|
|
return (buf);
|
|
fail:
|
|
log_warn("prepare_ls_update");
|
|
ibuf_free(buf);
|
|
return (NULL);
|
|
}
|
|
|
|
int
|
|
add_ls_update(struct ibuf *buf, struct iface *iface, void *data, u_int16_t len,
|
|
u_int16_t older)
|
|
{
|
|
size_t ageoff;
|
|
u_int16_t age;
|
|
|
|
if ((size_t)iface->mtu < sizeof(struct ip) + sizeof(struct ospf_hdr) +
|
|
sizeof(u_int32_t) + ibuf_size(buf) + len + MD5_DIGEST_LENGTH) {
|
|
/* start new packet unless this is the first LSA to pack */
|
|
if (ibuf_size(buf) > sizeof(struct ospf_hdr) +
|
|
sizeof(u_int32_t))
|
|
return (0);
|
|
}
|
|
|
|
ageoff = ibuf_size(buf);
|
|
if (ibuf_add(buf, data, len)) {
|
|
log_warn("add_ls_update");
|
|
return (0);
|
|
}
|
|
|
|
/* age LSA before sending it out */
|
|
memcpy(&age, data, sizeof(age));
|
|
age = ntohs(age);
|
|
if ((age += older + iface->transmit_delay) >= MAX_AGE)
|
|
age = MAX_AGE;
|
|
if (ibuf_set_n16(buf, ageoff, age) == -1) {
|
|
log_warn("add_ls_update");
|
|
return (0);
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
|
|
|
|
int
|
|
send_ls_update(struct ibuf *buf, struct iface *iface, struct in_addr addr,
|
|
u_int32_t nlsa)
|
|
{
|
|
struct sockaddr_in dst;
|
|
|
|
if (ibuf_set_n32(buf, sizeof(struct ospf_hdr), nlsa) == -1)
|
|
goto fail;
|
|
/* update authentication and calculate checksum */
|
|
if (auth_gen(buf, iface))
|
|
goto fail;
|
|
|
|
/* set destination */
|
|
dst.sin_family = AF_INET;
|
|
dst.sin_len = sizeof(struct sockaddr_in);
|
|
dst.sin_addr.s_addr = addr.s_addr;
|
|
|
|
if (send_packet(iface, buf, &dst) == -1)
|
|
goto fail;
|
|
|
|
ibuf_free(buf);
|
|
return (0);
|
|
fail:
|
|
log_warn("%s", __func__);
|
|
ibuf_free(buf);
|
|
return (-1);
|
|
}
|
|
|
|
void
|
|
recv_ls_update(struct nbr *nbr, char *buf, u_int16_t len)
|
|
{
|
|
struct lsa_hdr lsa;
|
|
u_int32_t nlsa;
|
|
|
|
if (len < sizeof(nlsa)) {
|
|
log_warnx("recv_ls_update: bad packet size, "
|
|
"neighbor ID %s (%s)", inet_ntoa(nbr->id),
|
|
nbr->iface->name);
|
|
return;
|
|
}
|
|
memcpy(&nlsa, buf, sizeof(nlsa));
|
|
nlsa = ntohl(nlsa);
|
|
buf += sizeof(nlsa);
|
|
len -= sizeof(nlsa);
|
|
|
|
switch (nbr->state) {
|
|
case NBR_STA_DOWN:
|
|
case NBR_STA_ATTEMPT:
|
|
case NBR_STA_INIT:
|
|
case NBR_STA_2_WAY:
|
|
case NBR_STA_XSTRT:
|
|
case NBR_STA_SNAP:
|
|
log_debug("recv_ls_update: packet ignored in state %s, "
|
|
"neighbor ID %s (%s)", nbr_state_name(nbr->state),
|
|
inet_ntoa(nbr->id), nbr->iface->name);
|
|
break;
|
|
case NBR_STA_XCHNG:
|
|
case NBR_STA_LOAD:
|
|
case NBR_STA_FULL:
|
|
for (; nlsa > 0 && len > 0; nlsa--) {
|
|
if (len < sizeof(lsa)) {
|
|
log_warnx("recv_ls_update: bad packet size, "
|
|
"neighbor ID %s (%s)", inet_ntoa(nbr->id),
|
|
nbr->iface->name);
|
|
return;
|
|
}
|
|
memcpy(&lsa, buf, sizeof(lsa));
|
|
if (len < ntohs(lsa.len)) {
|
|
log_warnx("recv_ls_update: bad packet size, "
|
|
"neighbor ID %s (%s)", inet_ntoa(nbr->id),
|
|
nbr->iface->name);
|
|
return;
|
|
}
|
|
ospfe_imsg_compose_rde(IMSG_LS_UPD, nbr->peerid, 0,
|
|
buf, ntohs(lsa.len));
|
|
buf += ntohs(lsa.len);
|
|
len -= ntohs(lsa.len);
|
|
}
|
|
if (nlsa > 0 || len > 0) {
|
|
log_warnx("recv_ls_update: bad packet size, "
|
|
"neighbor ID %s (%s)", inet_ntoa(nbr->id),
|
|
nbr->iface->name);
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
fatalx("recv_ls_update: unknown neighbor state");
|
|
}
|
|
}
|
|
|
|
/* link state retransmit list */
|
|
void
|
|
ls_retrans_list_add(struct nbr *nbr, struct lsa_hdr *lsa,
|
|
unsigned short timeout, unsigned short oneshot)
|
|
{
|
|
struct timeval tv;
|
|
struct lsa_entry *le;
|
|
struct lsa_ref *ref;
|
|
|
|
if ((ref = lsa_cache_get(lsa)) == NULL)
|
|
fatalx("King Bula sez: somebody forgot to lsa_cache_add");
|
|
|
|
if ((le = calloc(1, sizeof(*le))) == NULL)
|
|
fatal("ls_retrans_list_add");
|
|
|
|
le->le_ref = ref;
|
|
le->le_when = timeout;
|
|
le->le_oneshot = oneshot;
|
|
|
|
ls_retrans_list_insert(nbr, le);
|
|
|
|
if (!evtimer_pending(&nbr->ls_retrans_timer, NULL)) {
|
|
timerclear(&tv);
|
|
tv.tv_sec = TAILQ_FIRST(&nbr->ls_retrans_list)->le_when;
|
|
|
|
if (evtimer_add(&nbr->ls_retrans_timer, &tv) == -1)
|
|
fatal("ls_retrans_list_add");
|
|
}
|
|
}
|
|
|
|
int
|
|
ls_retrans_list_del(struct nbr *nbr, struct lsa_hdr *lsa_hdr)
|
|
{
|
|
struct lsa_entry *le;
|
|
|
|
if ((le = ls_retrans_list_get(nbr, lsa_hdr)) == NULL)
|
|
return (-1);
|
|
/*
|
|
* Compare LSA with the Ack by comparing not only the seq_num and
|
|
* checksum but also the age field. Since we only care about MAX_AGE
|
|
* vs. non-MAX_AGE LSA, a simple >= comparison is good enough. This
|
|
* ensures that a LSA withdrawal is not acked by a previous update.
|
|
*/
|
|
if (lsa_hdr->seq_num == le->le_ref->hdr.seq_num &&
|
|
lsa_hdr->ls_chksum == le->le_ref->hdr.ls_chksum &&
|
|
ntohs(lsa_hdr->age) >= ntohs(le->le_ref->hdr.age)) {
|
|
ls_retrans_list_free(nbr, le);
|
|
return (0);
|
|
}
|
|
|
|
return (-1);
|
|
}
|
|
|
|
struct lsa_entry *
|
|
ls_retrans_list_get(struct nbr *nbr, struct lsa_hdr *lsa_hdr)
|
|
{
|
|
struct lsa_entry *le;
|
|
|
|
TAILQ_FOREACH(le, &nbr->ls_retrans_list, entry) {
|
|
if ((lsa_hdr->type == le->le_ref->hdr.type) &&
|
|
(lsa_hdr->ls_id == le->le_ref->hdr.ls_id) &&
|
|
(lsa_hdr->adv_rtr == le->le_ref->hdr.adv_rtr))
|
|
return (le);
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
void
|
|
ls_retrans_list_insert(struct nbr *nbr, struct lsa_entry *new)
|
|
{
|
|
struct lsa_entry *le;
|
|
unsigned short when = new->le_when;
|
|
|
|
TAILQ_FOREACH(le, &nbr->ls_retrans_list, entry) {
|
|
if (when < le->le_when) {
|
|
new->le_when = when;
|
|
TAILQ_INSERT_BEFORE(le, new, entry);
|
|
nbr->ls_ret_cnt++;
|
|
return;
|
|
}
|
|
when -= le->le_when;
|
|
}
|
|
new->le_when = when;
|
|
TAILQ_INSERT_TAIL(&nbr->ls_retrans_list, new, entry);
|
|
nbr->ls_ret_cnt++;
|
|
}
|
|
|
|
void
|
|
ls_retrans_list_remove(struct nbr *nbr, struct lsa_entry *le)
|
|
{
|
|
struct timeval tv;
|
|
struct lsa_entry *next = TAILQ_NEXT(le, entry);
|
|
int reset = 0;
|
|
|
|
/* adjust timeout of next entry */
|
|
if (next)
|
|
next->le_when += le->le_when;
|
|
|
|
if (TAILQ_FIRST(&nbr->ls_retrans_list) == le &&
|
|
evtimer_pending(&nbr->ls_retrans_timer, NULL))
|
|
reset = 1;
|
|
|
|
TAILQ_REMOVE(&nbr->ls_retrans_list, le, entry);
|
|
nbr->ls_ret_cnt--;
|
|
|
|
if (reset && TAILQ_FIRST(&nbr->ls_retrans_list)) {
|
|
if (evtimer_del(&nbr->ls_retrans_timer) == -1)
|
|
fatal("ls_retrans_list_remove");
|
|
|
|
timerclear(&tv);
|
|
tv.tv_sec = TAILQ_FIRST(&nbr->ls_retrans_list)->le_when;
|
|
|
|
if (evtimer_add(&nbr->ls_retrans_timer, &tv) == -1)
|
|
fatal("ls_retrans_list_remove");
|
|
}
|
|
}
|
|
|
|
void
|
|
ls_retrans_list_free(struct nbr *nbr, struct lsa_entry *le)
|
|
{
|
|
ls_retrans_list_remove(nbr, le);
|
|
|
|
lsa_cache_put(le->le_ref, nbr);
|
|
free(le);
|
|
}
|
|
|
|
void
|
|
ls_retrans_list_clr(struct nbr *nbr)
|
|
{
|
|
struct lsa_entry *le;
|
|
|
|
while ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL)
|
|
ls_retrans_list_free(nbr, le);
|
|
|
|
nbr->ls_ret_cnt = 0;
|
|
}
|
|
|
|
void
|
|
ls_retrans_timer(int fd, short event, void *bula)
|
|
{
|
|
struct timeval tv;
|
|
struct timespec tp;
|
|
struct in_addr addr;
|
|
struct nbr *nbr = bula;
|
|
struct lsa_entry *le;
|
|
struct ibuf *buf;
|
|
time_t now;
|
|
int d;
|
|
u_int32_t nlsa = 0;
|
|
|
|
if ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL)
|
|
le->le_when = 0; /* timer fired */
|
|
else
|
|
return; /* queue empty, nothing to do */
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &tp);
|
|
now = tp.tv_sec;
|
|
|
|
if (nbr->iface->self == nbr) {
|
|
/*
|
|
* oneshot needs to be set for lsa queued for flooding,
|
|
* if oneshot is not set then the lsa needs to be converted
|
|
* because the router switched lately to DR or BDR
|
|
*/
|
|
if (le->le_oneshot && nbr->iface->state & IF_STA_DRORBDR)
|
|
inet_aton(AllSPFRouters, &addr);
|
|
else if (nbr->iface->state & IF_STA_DRORBDR) {
|
|
/*
|
|
* old retransmission needs to be converted into
|
|
* flood by rerunning the lsa_flood.
|
|
*/
|
|
lsa_flood(nbr->iface, nbr, &le->le_ref->hdr,
|
|
le->le_ref->data);
|
|
ls_retrans_list_free(nbr, le);
|
|
/* ls_retrans_list_free retriggers the timer */
|
|
return;
|
|
} else if (nbr->iface->type == IF_TYPE_POINTOPOINT)
|
|
memcpy(&addr, &nbr->addr, sizeof(addr));
|
|
else
|
|
inet_aton(AllDRouters, &addr);
|
|
} else
|
|
memcpy(&addr, &nbr->addr, sizeof(addr));
|
|
|
|
if ((buf = prepare_ls_update(nbr->iface)) == NULL) {
|
|
le->le_when = 1;
|
|
goto done;
|
|
}
|
|
|
|
while ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL &&
|
|
le->le_when == 0) {
|
|
d = now - le->le_ref->stamp;
|
|
if (d < 0)
|
|
d = 0;
|
|
else if (d > MAX_AGE)
|
|
d = MAX_AGE;
|
|
|
|
if (add_ls_update(buf, nbr->iface, le->le_ref->data,
|
|
le->le_ref->len, d) == 0) {
|
|
if (nlsa == 0) {
|
|
/* something bad happened retry later */
|
|
log_warnx("ls_retrans_timer: sending LS update "
|
|
"to neighbor ID %s (%s) failed",
|
|
inet_ntoa(nbr->id), nbr->iface->name);
|
|
TAILQ_REMOVE(&nbr->ls_retrans_list, le, entry);
|
|
nbr->ls_ret_cnt--;
|
|
le->le_when = nbr->iface->rxmt_interval;
|
|
ls_retrans_list_insert(nbr, le);
|
|
}
|
|
break;
|
|
}
|
|
nlsa++;
|
|
if (le->le_oneshot)
|
|
ls_retrans_list_free(nbr, le);
|
|
else {
|
|
TAILQ_REMOVE(&nbr->ls_retrans_list, le, entry);
|
|
nbr->ls_ret_cnt--;
|
|
le->le_when = nbr->iface->rxmt_interval;
|
|
ls_retrans_list_insert(nbr, le);
|
|
}
|
|
}
|
|
if (nlsa)
|
|
send_ls_update(buf, nbr->iface, addr, nlsa);
|
|
else
|
|
ibuf_free(buf);
|
|
|
|
done:
|
|
if ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL) {
|
|
timerclear(&tv);
|
|
tv.tv_sec = le->le_when;
|
|
|
|
if (evtimer_add(&nbr->ls_retrans_timer, &tv) == -1)
|
|
fatal("ls_retrans_timer");
|
|
}
|
|
}
|
|
|
|
LIST_HEAD(lsa_cache_head, lsa_ref);
|
|
|
|
struct lsa_cache {
|
|
struct lsa_cache_head *hashtbl;
|
|
u_int32_t hashmask;
|
|
} lsacache;
|
|
|
|
SIPHASH_KEY lsacachekey;
|
|
|
|
struct lsa_ref *lsa_cache_look(struct lsa_hdr *);
|
|
|
|
void
|
|
lsa_cache_init(u_int32_t hashsize)
|
|
{
|
|
u_int32_t hs, i;
|
|
|
|
for (hs = 1; hs < hashsize; hs <<= 1)
|
|
;
|
|
lsacache.hashtbl = calloc(hs, sizeof(struct lsa_cache_head));
|
|
if (lsacache.hashtbl == NULL)
|
|
fatal("lsa_cache_init");
|
|
|
|
for (i = 0; i < hs; i++)
|
|
LIST_INIT(&lsacache.hashtbl[i]);
|
|
arc4random_buf(&lsacachekey, sizeof(lsacachekey));
|
|
|
|
lsacache.hashmask = hs - 1;
|
|
}
|
|
|
|
static uint32_t
|
|
lsa_hash_hdr(const struct lsa_hdr *hdr)
|
|
{
|
|
return SipHash24(&lsacachekey, hdr, sizeof(*hdr));
|
|
}
|
|
|
|
struct lsa_ref *
|
|
lsa_cache_add(void *data, u_int16_t len)
|
|
{
|
|
struct lsa_cache_head *head;
|
|
struct lsa_ref *ref, *old;
|
|
struct timespec tp;
|
|
|
|
if ((ref = calloc(1, sizeof(*ref))) == NULL)
|
|
fatal("lsa_cache_add");
|
|
memcpy(&ref->hdr, data, sizeof(ref->hdr));
|
|
|
|
if ((old = lsa_cache_look(&ref->hdr))) {
|
|
free(ref);
|
|
old->refcnt++;
|
|
return (old);
|
|
}
|
|
|
|
if ((ref->data = malloc(len)) == NULL)
|
|
fatal("lsa_cache_add");
|
|
memcpy(ref->data, data, len);
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &tp);
|
|
ref->stamp = tp.tv_sec;
|
|
ref->len = len;
|
|
ref->refcnt = 1;
|
|
|
|
head = &lsacache.hashtbl[lsa_hash_hdr(&ref->hdr) & lsacache.hashmask];
|
|
LIST_INSERT_HEAD(head, ref, entry);
|
|
return (ref);
|
|
}
|
|
|
|
struct lsa_ref *
|
|
lsa_cache_get(struct lsa_hdr *lsa_hdr)
|
|
{
|
|
struct lsa_ref *ref;
|
|
|
|
ref = lsa_cache_look(lsa_hdr);
|
|
if (ref)
|
|
ref->refcnt++;
|
|
|
|
return (ref);
|
|
}
|
|
|
|
void
|
|
lsa_cache_put(struct lsa_ref *ref, struct nbr *nbr)
|
|
{
|
|
if (--ref->refcnt > 0)
|
|
return;
|
|
|
|
if (ntohs(ref->hdr.age) >= MAX_AGE)
|
|
ospfe_imsg_compose_rde(IMSG_LS_MAXAGE, nbr->peerid, 0,
|
|
ref->data, sizeof(struct lsa_hdr));
|
|
|
|
free(ref->data);
|
|
LIST_REMOVE(ref, entry);
|
|
free(ref);
|
|
}
|
|
|
|
struct lsa_ref *
|
|
lsa_cache_look(struct lsa_hdr *lsa_hdr)
|
|
{
|
|
struct lsa_cache_head *head;
|
|
struct lsa_ref *ref;
|
|
|
|
head = &lsacache.hashtbl[lsa_hash_hdr(lsa_hdr) & lsacache.hashmask];
|
|
|
|
LIST_FOREACH(ref, head, entry) {
|
|
if (memcmp(&ref->hdr, lsa_hdr, sizeof(*lsa_hdr)) == 0)
|
|
/* found match */
|
|
return (ref);
|
|
}
|
|
|
|
return (NULL);
|
|
}
|