xref: /openbsd-src/usr.sbin/ospfd/lsupdate.c (revision 4f4fe40bc9d06de09f4af934b87299ab1ee3cc66)
1*4f4fe40bSflorian /*	$OpenBSD: lsupdate.c,v 1.54 2024/08/21 15:18:00 florian Exp $ */
2204df0f8Sclaudio 
3204df0f8Sclaudio /*
4204df0f8Sclaudio  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
5367f601bSnorby  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
6204df0f8Sclaudio  *
7204df0f8Sclaudio  * Permission to use, copy, modify, and distribute this software for any
8204df0f8Sclaudio  * purpose with or without fee is hereby granted, provided that the above
9204df0f8Sclaudio  * copyright notice and this permission notice appear in all copies.
10204df0f8Sclaudio  *
11204df0f8Sclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12204df0f8Sclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13204df0f8Sclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14204df0f8Sclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15204df0f8Sclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16204df0f8Sclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17204df0f8Sclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18204df0f8Sclaudio  */
19204df0f8Sclaudio 
20204df0f8Sclaudio #include <sys/types.h>
21204df0f8Sclaudio #include <sys/socket.h>
22204df0f8Sclaudio #include <netinet/in.h>
23204df0f8Sclaudio #include <arpa/inet.h>
24204df0f8Sclaudio 
25204df0f8Sclaudio #include <stdlib.h>
2653e1908aSstevesk #include <string.h>
275db4cdaaStedu #include <siphash.h>
28204df0f8Sclaudio 
29204df0f8Sclaudio #include "ospf.h"
30204df0f8Sclaudio #include "ospfd.h"
31204df0f8Sclaudio #include "log.h"
32204df0f8Sclaudio #include "ospfe.h"
33204df0f8Sclaudio #include "rde.h"
34204df0f8Sclaudio 
35e39620e5Snicm struct ibuf *prepare_ls_update(struct iface *);
36e39620e5Snicm int	add_ls_update(struct ibuf *, struct iface *, void *, u_int16_t,
3729921c50Sclaudio 	    u_int16_t);
38e39620e5Snicm int	send_ls_update(struct ibuf *, struct iface *, struct in_addr, u_int32_t);
3932a640a4Sclaudio 
4032a640a4Sclaudio void	ls_retrans_list_insert(struct nbr *, struct lsa_entry *);
4132a640a4Sclaudio void	ls_retrans_list_remove(struct nbr *, struct lsa_entry *);
4232a640a4Sclaudio 
43204df0f8Sclaudio /* link state update packet handling */
44204df0f8Sclaudio int
45204df0f8Sclaudio lsa_flood(struct iface *iface, struct nbr *originator, struct lsa_hdr *lsa_hdr,
4659eccb25Snorby     void *data)
47204df0f8Sclaudio {
48204df0f8Sclaudio 	struct nbr		*nbr;
49cb75af8bSclaudio 	struct lsa_entry	*le = NULL;
50204df0f8Sclaudio 	int			 queued = 0, dont_ack = 0;
51204df0f8Sclaudio 	int			 r;
52204df0f8Sclaudio 
53204df0f8Sclaudio 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
54204df0f8Sclaudio 		if (nbr == iface->self)
55204df0f8Sclaudio 			continue;
56204df0f8Sclaudio 		if (!(nbr->state & NBR_STA_FLOOD))
57204df0f8Sclaudio 			continue;
58204df0f8Sclaudio 
59bfaba638Sclaudio 		if (iface->state & IF_STA_DROTHER && !queued)
6064896ccbSclaudio 			while ((le = ls_retrans_list_get(iface->self, lsa_hdr)))
61bfaba638Sclaudio 			    ls_retrans_list_free(iface->self, le);
62cb75af8bSclaudio 
6364896ccbSclaudio 		while ((le = ls_retrans_list_get(nbr, lsa_hdr)))
64204df0f8Sclaudio 			ls_retrans_list_free(nbr, le);
65204df0f8Sclaudio 
66204df0f8Sclaudio 		if (!(nbr->state & NBR_STA_FULL) &&
67204df0f8Sclaudio 		    (le = ls_req_list_get(nbr, lsa_hdr)) != NULL) {
68204df0f8Sclaudio 			r = lsa_newer(lsa_hdr, le->le_lsa);
69204df0f8Sclaudio 			if (r > 0) {
70204df0f8Sclaudio 				/* to flood LSA is newer than requested */
71204df0f8Sclaudio 				ls_req_list_free(nbr, le);
72204df0f8Sclaudio 				/* new needs to be flooded */
73204df0f8Sclaudio 			} else if (r < 0) {
74204df0f8Sclaudio 				/* to flood LSA is older than requested */
75204df0f8Sclaudio 				continue;
76204df0f8Sclaudio 			} else {
77204df0f8Sclaudio 				/* LSA are equal */
78204df0f8Sclaudio 				ls_req_list_free(nbr, le);
79204df0f8Sclaudio 				continue;
80204df0f8Sclaudio 			}
81204df0f8Sclaudio 		}
82204df0f8Sclaudio 
83204df0f8Sclaudio 		if (nbr == originator) {
84204df0f8Sclaudio 			dont_ack++;
85204df0f8Sclaudio 			continue;
86204df0f8Sclaudio 		}
87204df0f8Sclaudio 
881d38c082Sclaudio 		/* non DR or BDR router keep all lsa in one retrans list */
89cb75af8bSclaudio 		if (iface->state & IF_STA_DROTHER) {
90cb75af8bSclaudio 			if (!queued)
9132a640a4Sclaudio 				ls_retrans_list_add(iface->self, data,
9232a640a4Sclaudio 				    iface->rxmt_interval, 0);
931d38c082Sclaudio 			queued = 1;
9426745d1dSclaudio 		} else {
9532a640a4Sclaudio 			ls_retrans_list_add(nbr, data, iface->rxmt_interval, 0);
961d38c082Sclaudio 			queued = 1;
971d38c082Sclaudio 		}
98204df0f8Sclaudio 	}
99cb75af8bSclaudio 
100204df0f8Sclaudio 	if (!queued)
101204df0f8Sclaudio 		return (0);
102204df0f8Sclaudio 
10369d17b7aSclaudio 	if (iface == originator->iface && iface->self != originator) {
104204df0f8Sclaudio 		if (iface->dr == originator || iface->bdr == originator)
105204df0f8Sclaudio 			return (0);
106204df0f8Sclaudio 		if (iface->state & IF_STA_BACKUP)
107204df0f8Sclaudio 			return (0);
108204df0f8Sclaudio 		dont_ack++;
109204df0f8Sclaudio 	}
110204df0f8Sclaudio 
11132a640a4Sclaudio 	/*
11232a640a4Sclaudio 	 * initial flood needs to be queued separately, timeout is zero
11332a640a4Sclaudio 	 * and oneshot has to be set because the retransimssion queues
11432a640a4Sclaudio 	 * are already loaded.
11532a640a4Sclaudio 	 */
116204df0f8Sclaudio 	switch (iface->type) {
117204df0f8Sclaudio 	case IF_TYPE_POINTOPOINT:
118204df0f8Sclaudio 	case IF_TYPE_BROADCAST:
11932a640a4Sclaudio 		ls_retrans_list_add(iface->self, data, 0, 1);
120204df0f8Sclaudio 		break;
121204df0f8Sclaudio 	case IF_TYPE_NBMA:
122204df0f8Sclaudio 	case IF_TYPE_POINTOMULTIPOINT:
123204df0f8Sclaudio 	case IF_TYPE_VIRTUALLINK:
124204df0f8Sclaudio 		LIST_FOREACH(nbr, &iface->nbr_list, entry) {
125204df0f8Sclaudio 			if (nbr == iface->self)
126204df0f8Sclaudio 				continue;
127204df0f8Sclaudio 			if (!(nbr->state & NBR_STA_FLOOD))
128204df0f8Sclaudio 				continue;
129204df0f8Sclaudio 			if (!TAILQ_EMPTY(&nbr->ls_retrans_list)) {
130204df0f8Sclaudio 				le = TAILQ_LAST(&nbr->ls_retrans_list,
131204df0f8Sclaudio 				    lsa_head);
132204df0f8Sclaudio 				if (lsa_hdr->type != le->le_lsa->type ||
133204df0f8Sclaudio 				    lsa_hdr->ls_id != le->le_lsa->ls_id ||
134204df0f8Sclaudio 				    lsa_hdr->adv_rtr != le->le_lsa->adv_rtr)
135204df0f8Sclaudio 					continue;
136204df0f8Sclaudio 			}
13732a640a4Sclaudio 			ls_retrans_list_add(nbr, data, 0, 1);
138204df0f8Sclaudio 		}
139204df0f8Sclaudio 		break;
140204df0f8Sclaudio 	default:
141204df0f8Sclaudio 		fatalx("lsa_flood: unknown interface type");
142204df0f8Sclaudio 	}
143204df0f8Sclaudio 
144204df0f8Sclaudio 	return (dont_ack == 2);
145204df0f8Sclaudio }
146204df0f8Sclaudio 
147e39620e5Snicm struct ibuf *
14832a640a4Sclaudio prepare_ls_update(struct iface *iface)
149204df0f8Sclaudio {
150e39620e5Snicm 	struct ibuf		*buf;
151204df0f8Sclaudio 
1529c09f078Sclaudio 	if ((buf = ibuf_dynamic(iface->mtu - sizeof(struct ip),
1539c09f078Sclaudio 	    IP_MAXPACKET - sizeof(struct ip))) == NULL)
15498c612a2Snorby 		fatal("prepare_ls_update");
155204df0f8Sclaudio 
156204df0f8Sclaudio 	/* OSPF header */
1570491ce75Sclaudio 	if (gen_ospf_hdr(buf, iface, PACKET_TYPE_LS_UPDATE))
1580491ce75Sclaudio 		goto fail;
159204df0f8Sclaudio 
16032a640a4Sclaudio 	/* reserve space for number of lsa field */
16143e70bb4Sclaudio 	if (ibuf_add_zero(buf, sizeof(u_int32_t)) == -1)
1620491ce75Sclaudio 		goto fail;
163204df0f8Sclaudio 
16432a640a4Sclaudio 	return (buf);
16532a640a4Sclaudio fail:
16632a640a4Sclaudio 	log_warn("prepare_ls_update");
167e39620e5Snicm 	ibuf_free(buf);
16832a640a4Sclaudio 	return (NULL);
16932a640a4Sclaudio }
17032a640a4Sclaudio 
17132a640a4Sclaudio int
172e39620e5Snicm add_ls_update(struct ibuf *buf, struct iface *iface, void *data, u_int16_t len,
17364896ccbSclaudio     u_int16_t older)
17432a640a4Sclaudio {
17507669e8bSclaudio 	size_t		ageoff;
17632a640a4Sclaudio 	u_int16_t	age;
17732a640a4Sclaudio 
1789c09f078Sclaudio 	if ((size_t)iface->mtu < sizeof(struct ip) + sizeof(struct ospf_hdr) +
1799c09f078Sclaudio 	    sizeof(u_int32_t) + ibuf_size(buf) + len + MD5_DIGEST_LENGTH) {
1809c09f078Sclaudio 		/* start new packet unless this is the first LSA to pack */
1819c09f078Sclaudio 		if (ibuf_size(buf) > sizeof(struct ospf_hdr) +
1829c09f078Sclaudio 		    sizeof(u_int32_t))
18332a640a4Sclaudio 			return (0);
1849c09f078Sclaudio 	}
18532a640a4Sclaudio 
18607669e8bSclaudio 	ageoff = ibuf_size(buf);
187e39620e5Snicm 	if (ibuf_add(buf, data, len)) {
18832a640a4Sclaudio 		log_warn("add_ls_update");
18932a640a4Sclaudio 		return (0);
19032a640a4Sclaudio 	}
191204df0f8Sclaudio 
192611e3786Sclaudio 	/* age LSA before sending it out */
1930491ce75Sclaudio 	memcpy(&age, data, sizeof(age));
194204df0f8Sclaudio 	age = ntohs(age);
19564896ccbSclaudio 	if ((age += older + iface->transmit_delay) >= MAX_AGE)
196cd3874a6Sclaudio 		age = MAX_AGE;
19743e70bb4Sclaudio 	if (ibuf_set_n16(buf, ageoff, age) == -1) {
19843e70bb4Sclaudio 		log_warn("add_ls_update");
19943e70bb4Sclaudio 		return (0);
20043e70bb4Sclaudio 	}
201204df0f8Sclaudio 
20232a640a4Sclaudio 	return (1);
20332a640a4Sclaudio }
20432a640a4Sclaudio 
20532a640a4Sclaudio int
206e39620e5Snicm send_ls_update(struct ibuf *buf, struct iface *iface, struct in_addr addr,
20732a640a4Sclaudio     u_int32_t nlsa)
20832a640a4Sclaudio {
20932a640a4Sclaudio 	struct sockaddr_in	 dst;
21032a640a4Sclaudio 
21143e70bb4Sclaudio 	if (ibuf_set_n32(buf, sizeof(struct ospf_hdr), nlsa) == -1)
21243e70bb4Sclaudio 		goto fail;
213204df0f8Sclaudio 	/* update authentication and calculate checksum */
2140491ce75Sclaudio 	if (auth_gen(buf, iface))
2150491ce75Sclaudio 		goto fail;
216204df0f8Sclaudio 
21732a640a4Sclaudio 	/* set destination */
21832a640a4Sclaudio 	dst.sin_family = AF_INET;
21932a640a4Sclaudio 	dst.sin_len = sizeof(struct sockaddr_in);
22032a640a4Sclaudio 	dst.sin_addr.s_addr = addr.s_addr;
22132a640a4Sclaudio 
22225a742ccSremi 	if (send_packet(iface, buf, &dst) == -1)
22325a742ccSremi 		goto fail;
224204df0f8Sclaudio 
225e39620e5Snicm 	ibuf_free(buf);
22625a742ccSremi 	return (0);
2270491ce75Sclaudio fail:
22825a742ccSremi 	log_warn("%s", __func__);
229e39620e5Snicm 	ibuf_free(buf);
2300491ce75Sclaudio 	return (-1);
231204df0f8Sclaudio }
232204df0f8Sclaudio 
233204df0f8Sclaudio void
234204df0f8Sclaudio recv_ls_update(struct nbr *nbr, char *buf, u_int16_t len)
235204df0f8Sclaudio {
236204df0f8Sclaudio 	struct lsa_hdr		 lsa;
237204df0f8Sclaudio 	u_int32_t		 nlsa;
238204df0f8Sclaudio 
239204df0f8Sclaudio 	if (len < sizeof(nlsa)) {
2409cc19ef1Ssthen 		log_warnx("recv_ls_update: bad packet size, "
2419cc19ef1Ssthen 		    "neighbor ID %s (%s)", inet_ntoa(nbr->id),
2429cc19ef1Ssthen 		    nbr->iface->name);
243204df0f8Sclaudio 		return;
244204df0f8Sclaudio 	}
245204df0f8Sclaudio 	memcpy(&nlsa, buf, sizeof(nlsa));
246204df0f8Sclaudio 	nlsa = ntohl(nlsa);
247204df0f8Sclaudio 	buf += sizeof(nlsa);
248204df0f8Sclaudio 	len -= sizeof(nlsa);
249204df0f8Sclaudio 
250204df0f8Sclaudio 	switch (nbr->state) {
251204df0f8Sclaudio 	case NBR_STA_DOWN:
252204df0f8Sclaudio 	case NBR_STA_ATTEMPT:
253204df0f8Sclaudio 	case NBR_STA_INIT:
254204df0f8Sclaudio 	case NBR_STA_2_WAY:
255204df0f8Sclaudio 	case NBR_STA_XSTRT:
256204df0f8Sclaudio 	case NBR_STA_SNAP:
257204df0f8Sclaudio 		log_debug("recv_ls_update: packet ignored in state %s, "
2589cc19ef1Ssthen 		    "neighbor ID %s (%s)", nbr_state_name(nbr->state),
2599cc19ef1Ssthen 		    inet_ntoa(nbr->id), nbr->iface->name);
260204df0f8Sclaudio 		break;
261204df0f8Sclaudio 	case NBR_STA_XCHNG:
262204df0f8Sclaudio 	case NBR_STA_LOAD:
263204df0f8Sclaudio 	case NBR_STA_FULL:
264204df0f8Sclaudio 		for (; nlsa > 0 && len > 0; nlsa--) {
265204df0f8Sclaudio 			if (len < sizeof(lsa)) {
266204df0f8Sclaudio 				log_warnx("recv_ls_update: bad packet size, "
2679cc19ef1Ssthen 				    "neighbor ID %s (%s)", inet_ntoa(nbr->id),
2689cc19ef1Ssthen 				    nbr->iface->name);
269204df0f8Sclaudio 				return;
270204df0f8Sclaudio 			}
271204df0f8Sclaudio 			memcpy(&lsa, buf, sizeof(lsa));
272204df0f8Sclaudio 			if (len < ntohs(lsa.len)) {
273204df0f8Sclaudio 				log_warnx("recv_ls_update: bad packet size, "
2749cc19ef1Ssthen 				    "neighbor ID %s (%s)", inet_ntoa(nbr->id),
2759cc19ef1Ssthen 				    nbr->iface->name);
276204df0f8Sclaudio 				return;
277204df0f8Sclaudio 			}
2786efdfed8Sclaudio 			ospfe_imsg_compose_rde(IMSG_LS_UPD, nbr->peerid, 0,
2796efdfed8Sclaudio 			    buf, ntohs(lsa.len));
280204df0f8Sclaudio 			buf += ntohs(lsa.len);
281204df0f8Sclaudio 			len -= ntohs(lsa.len);
282204df0f8Sclaudio 		}
283204df0f8Sclaudio 		if (nlsa > 0 || len > 0) {
284204df0f8Sclaudio 			log_warnx("recv_ls_update: bad packet size, "
2859cc19ef1Ssthen 			    "neighbor ID %s (%s)", inet_ntoa(nbr->id),
2869cc19ef1Ssthen 			    nbr->iface->name);
287204df0f8Sclaudio 			return;
288204df0f8Sclaudio 		}
289204df0f8Sclaudio 		break;
290204df0f8Sclaudio 	default:
291204df0f8Sclaudio 		fatalx("recv_ls_update: unknown neighbor state");
292204df0f8Sclaudio 	}
293204df0f8Sclaudio }
294204df0f8Sclaudio 
295204df0f8Sclaudio /* link state retransmit list */
296cb75af8bSclaudio void
29732a640a4Sclaudio ls_retrans_list_add(struct nbr *nbr, struct lsa_hdr *lsa,
29832a640a4Sclaudio     unsigned short timeout, unsigned short oneshot)
299204df0f8Sclaudio {
300cb75af8bSclaudio 	struct timeval		 tv;
301204df0f8Sclaudio 	struct lsa_entry	*le;
302204df0f8Sclaudio 	struct lsa_ref		*ref;
303204df0f8Sclaudio 
304cb75af8bSclaudio 	if ((ref = lsa_cache_get(lsa)) == NULL)
305cb75af8bSclaudio 		fatalx("King Bula sez: somebody forgot to lsa_cache_add");
306204df0f8Sclaudio 
307204df0f8Sclaudio 	if ((le = calloc(1, sizeof(*le))) == NULL)
308204df0f8Sclaudio 		fatal("ls_retrans_list_add");
309204df0f8Sclaudio 
310204df0f8Sclaudio 	le->le_ref = ref;
31132a640a4Sclaudio 	le->le_when = timeout;
31232a640a4Sclaudio 	le->le_oneshot = oneshot;
31332a640a4Sclaudio 
31432a640a4Sclaudio 	ls_retrans_list_insert(nbr, le);
315204df0f8Sclaudio 
316cb75af8bSclaudio 	if (!evtimer_pending(&nbr->ls_retrans_timer, NULL)) {
317cb75af8bSclaudio 		timerclear(&tv);
31832a640a4Sclaudio 		tv.tv_sec = TAILQ_FIRST(&nbr->ls_retrans_list)->le_when;
319cb75af8bSclaudio 
320cb75af8bSclaudio 		if (evtimer_add(&nbr->ls_retrans_timer, &tv) == -1)
3213aede346Sclaudio 			fatal("ls_retrans_list_add");
322cb75af8bSclaudio 	}
323204df0f8Sclaudio }
324204df0f8Sclaudio 
325204df0f8Sclaudio int
326204df0f8Sclaudio ls_retrans_list_del(struct nbr *nbr, struct lsa_hdr *lsa_hdr)
327204df0f8Sclaudio {
328204df0f8Sclaudio 	struct lsa_entry	*le;
329204df0f8Sclaudio 
33064896ccbSclaudio 	if ((le = ls_retrans_list_get(nbr, lsa_hdr)) == NULL)
331cb75af8bSclaudio 		return (-1);
3328e73b4e2Sbluhm 	/*
3338e73b4e2Sbluhm 	 * Compare LSA with the Ack by comparing not only the seq_num and
3348e73b4e2Sbluhm 	 * checksum but also the age field.  Since we only care about MAX_AGE
3358e73b4e2Sbluhm 	 * vs. non-MAX_AGE LSA, a simple >= comparison is good enough.  This
3368e73b4e2Sbluhm 	 * ensures that a LSA withdrawal is not acked by a previous update.
3378e73b4e2Sbluhm 	 */
338204df0f8Sclaudio 	if (lsa_hdr->seq_num == le->le_ref->hdr.seq_num &&
3398e73b4e2Sbluhm 	    lsa_hdr->ls_chksum == le->le_ref->hdr.ls_chksum &&
3408e73b4e2Sbluhm 	    ntohs(lsa_hdr->age) >= ntohs(le->le_ref->hdr.age)) {
341204df0f8Sclaudio 		ls_retrans_list_free(nbr, le);
342204df0f8Sclaudio 		return (0);
343204df0f8Sclaudio 	}
344204df0f8Sclaudio 
345cb75af8bSclaudio 	return (-1);
346204df0f8Sclaudio }
347204df0f8Sclaudio 
348204df0f8Sclaudio struct lsa_entry *
349204df0f8Sclaudio ls_retrans_list_get(struct nbr *nbr, struct lsa_hdr *lsa_hdr)
350204df0f8Sclaudio {
351204df0f8Sclaudio 	struct lsa_entry	*le;
352204df0f8Sclaudio 
353204df0f8Sclaudio 	TAILQ_FOREACH(le, &nbr->ls_retrans_list, entry) {
354204df0f8Sclaudio 		if ((lsa_hdr->type == le->le_ref->hdr.type) &&
355204df0f8Sclaudio 		    (lsa_hdr->ls_id == le->le_ref->hdr.ls_id) &&
356204df0f8Sclaudio 		    (lsa_hdr->adv_rtr == le->le_ref->hdr.adv_rtr))
357204df0f8Sclaudio 			return (le);
358204df0f8Sclaudio 	}
359204df0f8Sclaudio 	return (NULL);
360204df0f8Sclaudio }
361204df0f8Sclaudio 
362204df0f8Sclaudio void
36332a640a4Sclaudio ls_retrans_list_insert(struct nbr *nbr, struct lsa_entry *new)
36432a640a4Sclaudio {
36532a640a4Sclaudio 	struct lsa_entry	*le;
36632a640a4Sclaudio 	unsigned short		 when = new->le_when;
36732a640a4Sclaudio 
36832a640a4Sclaudio 	TAILQ_FOREACH(le, &nbr->ls_retrans_list, entry) {
36932a640a4Sclaudio 		if (when < le->le_when) {
37032a640a4Sclaudio 			new->le_when = when;
37132a640a4Sclaudio 			TAILQ_INSERT_BEFORE(le, new, entry);
3722b5aa3c5Sclaudio 			nbr->ls_ret_cnt++;
37332a640a4Sclaudio 			return;
37432a640a4Sclaudio 		}
37532a640a4Sclaudio 		when -= le->le_when;
37632a640a4Sclaudio 	}
37732a640a4Sclaudio 	new->le_when = when;
37832a640a4Sclaudio 	TAILQ_INSERT_TAIL(&nbr->ls_retrans_list, new, entry);
3791ba81fefSnorby 	nbr->ls_ret_cnt++;
38032a640a4Sclaudio }
38132a640a4Sclaudio 
38232a640a4Sclaudio void
38332a640a4Sclaudio ls_retrans_list_remove(struct nbr *nbr, struct lsa_entry *le)
38432a640a4Sclaudio {
38532a640a4Sclaudio 	struct timeval		 tv;
38632a640a4Sclaudio 	struct lsa_entry	*next = TAILQ_NEXT(le, entry);
38732a640a4Sclaudio 	int			 reset = 0;
38832a640a4Sclaudio 
38932a640a4Sclaudio 	/* adjust timeout of next entry */
39032a640a4Sclaudio 	if (next)
39132a640a4Sclaudio 		next->le_when += le->le_when;
39232a640a4Sclaudio 
39332a640a4Sclaudio 	if (TAILQ_FIRST(&nbr->ls_retrans_list) == le &&
39432a640a4Sclaudio 	    evtimer_pending(&nbr->ls_retrans_timer, NULL))
39532a640a4Sclaudio 		reset = 1;
39632a640a4Sclaudio 
39732a640a4Sclaudio 	TAILQ_REMOVE(&nbr->ls_retrans_list, le, entry);
3981ba81fefSnorby 	nbr->ls_ret_cnt--;
39932a640a4Sclaudio 
40032a640a4Sclaudio 	if (reset && TAILQ_FIRST(&nbr->ls_retrans_list)) {
4013aede346Sclaudio 		if (evtimer_del(&nbr->ls_retrans_timer) == -1)
4023aede346Sclaudio 			fatal("ls_retrans_list_remove");
40332a640a4Sclaudio 
40432a640a4Sclaudio 		timerclear(&tv);
40532a640a4Sclaudio 		tv.tv_sec = TAILQ_FIRST(&nbr->ls_retrans_list)->le_when;
40632a640a4Sclaudio 
40732a640a4Sclaudio 		if (evtimer_add(&nbr->ls_retrans_timer, &tv) == -1)
4083aede346Sclaudio 			fatal("ls_retrans_list_remove");
40932a640a4Sclaudio 	}
41032a640a4Sclaudio }
41132a640a4Sclaudio 
41232a640a4Sclaudio void
413204df0f8Sclaudio ls_retrans_list_free(struct nbr *nbr, struct lsa_entry *le)
414204df0f8Sclaudio {
41532a640a4Sclaudio 	ls_retrans_list_remove(nbr, le);
416204df0f8Sclaudio 
417204df0f8Sclaudio 	lsa_cache_put(le->le_ref, nbr);
418204df0f8Sclaudio 	free(le);
419204df0f8Sclaudio }
420204df0f8Sclaudio 
421204df0f8Sclaudio void
422204df0f8Sclaudio ls_retrans_list_clr(struct nbr *nbr)
423204df0f8Sclaudio {
424204df0f8Sclaudio 	struct lsa_entry	*le;
425204df0f8Sclaudio 
426204df0f8Sclaudio 	while ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL)
427204df0f8Sclaudio 		ls_retrans_list_free(nbr, le);
4281ba81fefSnorby 
4291ba81fefSnorby 	nbr->ls_ret_cnt = 0;
430204df0f8Sclaudio }
431204df0f8Sclaudio 
432cb75af8bSclaudio void
433cb75af8bSclaudio ls_retrans_timer(int fd, short event, void *bula)
434cb75af8bSclaudio {
435cb75af8bSclaudio 	struct timeval		 tv;
43664896ccbSclaudio 	struct timespec		 tp;
437cb75af8bSclaudio 	struct in_addr		 addr;
438cb75af8bSclaudio 	struct nbr		*nbr = bula;
439cb75af8bSclaudio 	struct lsa_entry	*le;
440e39620e5Snicm 	struct ibuf		*buf;
44164896ccbSclaudio 	time_t			 now;
44264896ccbSclaudio 	int			 d;
44332a640a4Sclaudio 	u_int32_t		 nlsa = 0;
44432a640a4Sclaudio 
44532a640a4Sclaudio 	if ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL)
44632a640a4Sclaudio 		le->le_when = 0;	/* timer fired */
44732a640a4Sclaudio 	else
44832a640a4Sclaudio 		return;			/* queue empty, nothing to do */
449cb75af8bSclaudio 
45064896ccbSclaudio 	clock_gettime(CLOCK_MONOTONIC, &tp);
45164896ccbSclaudio 	now = tp.tv_sec;
45264896ccbSclaudio 
453cb75af8bSclaudio 	if (nbr->iface->self == nbr) {
454cb75af8bSclaudio 		/*
45532a640a4Sclaudio 		 * oneshot needs to be set for lsa queued for flooding,
45632a640a4Sclaudio 		 * if oneshot is not set then the lsa needs to be converted
45732a640a4Sclaudio 		 * because the router switched lately to DR or BDR
458cb75af8bSclaudio 		 */
45932a640a4Sclaudio 		if (le->le_oneshot && nbr->iface->state & IF_STA_DRORBDR)
460*4f4fe40bSflorian 			inet_pton(AF_INET, AllSPFRouters, &addr);
46132a640a4Sclaudio 		else if (nbr->iface->state & IF_STA_DRORBDR) {
46232a640a4Sclaudio 			/*
46332a640a4Sclaudio 			 * old retransmission needs to be converted into
46432a640a4Sclaudio 			 * flood by rerunning the lsa_flood.
46532a640a4Sclaudio 			 */
466cb75af8bSclaudio 			lsa_flood(nbr->iface, nbr, &le->le_ref->hdr,
46759eccb25Snorby 			    le->le_ref->data);
468cb75af8bSclaudio 			ls_retrans_list_free(nbr, le);
46932a640a4Sclaudio 			/* ls_retrans_list_free retriggers the timer */
470cb75af8bSclaudio 			return;
47150cd5907Smarkus 		} else if (nbr->iface->type == IF_TYPE_POINTOPOINT)
472ef209401Sremi 			memcpy(&addr, &nbr->addr, sizeof(addr));
47350cd5907Smarkus 		else
474*4f4fe40bSflorian 			inet_pton(AF_INET, AllDRouters, &addr);
475cb75af8bSclaudio 	} else
476cb75af8bSclaudio 		memcpy(&addr, &nbr->addr, sizeof(addr));
477cb75af8bSclaudio 
47832a640a4Sclaudio 	if ((buf = prepare_ls_update(nbr->iface)) == NULL) {
47932a640a4Sclaudio 		le->le_when = 1;
48032a640a4Sclaudio 		goto done;
48132a640a4Sclaudio 	}
482cb75af8bSclaudio 
48332a640a4Sclaudio 	while ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL &&
48432a640a4Sclaudio 	    le->le_when == 0) {
48564896ccbSclaudio 		d = now - le->le_ref->stamp;
48664896ccbSclaudio 		if (d < 0)
48764896ccbSclaudio 			d = 0;
48864896ccbSclaudio 		else if (d > MAX_AGE)
48964896ccbSclaudio 			d = MAX_AGE;
49064896ccbSclaudio 
49132a640a4Sclaudio 		if (add_ls_update(buf, nbr->iface, le->le_ref->data,
4929c09f078Sclaudio 		    le->le_ref->len, d) == 0) {
4939c09f078Sclaudio 			if (nlsa == 0) {
49457ee193cSjca 				/* something bad happened retry later */
4959c09f078Sclaudio 				log_warnx("ls_retrans_timer: sending LS update "
4969cc19ef1Ssthen 				    "to neighbor ID %s (%s) failed",
4979cc19ef1Ssthen 				    inet_ntoa(nbr->id), nbr->iface->name);
4989c09f078Sclaudio 				TAILQ_REMOVE(&nbr->ls_retrans_list, le, entry);
4999c09f078Sclaudio 				nbr->ls_ret_cnt--;
5009c09f078Sclaudio 				le->le_when = nbr->iface->rxmt_interval;
5019c09f078Sclaudio 				ls_retrans_list_insert(nbr, le);
5029c09f078Sclaudio 			}
50332a640a4Sclaudio 			break;
5049c09f078Sclaudio 		}
50532a640a4Sclaudio 		nlsa++;
50632a640a4Sclaudio 		if (le->le_oneshot)
50732a640a4Sclaudio 			ls_retrans_list_free(nbr, le);
50832a640a4Sclaudio 		else {
50932a640a4Sclaudio 			TAILQ_REMOVE(&nbr->ls_retrans_list, le, entry);
5102b5aa3c5Sclaudio 			nbr->ls_ret_cnt--;
51132a640a4Sclaudio 			le->le_when = nbr->iface->rxmt_interval;
51232a640a4Sclaudio 			ls_retrans_list_insert(nbr, le);
51332a640a4Sclaudio 		}
51432a640a4Sclaudio 	}
5159c09f078Sclaudio 	if (nlsa)
51632a640a4Sclaudio 		send_ls_update(buf, nbr->iface, addr, nlsa);
5179c09f078Sclaudio 	else
5189c09f078Sclaudio 		ibuf_free(buf);
51932a640a4Sclaudio 
52032a640a4Sclaudio done:
521cb75af8bSclaudio 	if ((le = TAILQ_FIRST(&nbr->ls_retrans_list)) != NULL) {
522cb75af8bSclaudio 		timerclear(&tv);
52332a640a4Sclaudio 		tv.tv_sec = le->le_when;
524cb75af8bSclaudio 
525cb75af8bSclaudio 		if (evtimer_add(&nbr->ls_retrans_timer, &tv) == -1)
5263aede346Sclaudio 			fatal("ls_retrans_timer");
527cb75af8bSclaudio 	}
528cb75af8bSclaudio }
529cb75af8bSclaudio 
530204df0f8Sclaudio LIST_HEAD(lsa_cache_head, lsa_ref);
531204df0f8Sclaudio 
532204df0f8Sclaudio struct lsa_cache {
533204df0f8Sclaudio 	struct lsa_cache_head	*hashtbl;
534204df0f8Sclaudio 	u_int32_t		 hashmask;
535204df0f8Sclaudio } lsacache;
536204df0f8Sclaudio 
5375db4cdaaStedu SIPHASH_KEY lsacachekey;
5385db4cdaaStedu 
539204df0f8Sclaudio struct lsa_ref		*lsa_cache_look(struct lsa_hdr *);
540204df0f8Sclaudio 
541204df0f8Sclaudio void
542204df0f8Sclaudio lsa_cache_init(u_int32_t hashsize)
543204df0f8Sclaudio {
544204df0f8Sclaudio 	u_int32_t        hs, i;
545204df0f8Sclaudio 
546204df0f8Sclaudio 	for (hs = 1; hs < hashsize; hs <<= 1)
547204df0f8Sclaudio 		;
548204df0f8Sclaudio 	lsacache.hashtbl = calloc(hs, sizeof(struct lsa_cache_head));
549204df0f8Sclaudio 	if (lsacache.hashtbl == NULL)
550204df0f8Sclaudio 		fatal("lsa_cache_init");
551204df0f8Sclaudio 
552204df0f8Sclaudio 	for (i = 0; i < hs; i++)
553204df0f8Sclaudio 		LIST_INIT(&lsacache.hashtbl[i]);
5545db4cdaaStedu 	arc4random_buf(&lsacachekey, sizeof(lsacachekey));
555204df0f8Sclaudio 
556204df0f8Sclaudio 	lsacache.hashmask = hs - 1;
557204df0f8Sclaudio }
558204df0f8Sclaudio 
559d4b7cca4Stedu static uint32_t
5605db4cdaaStedu lsa_hash_hdr(const struct lsa_hdr *hdr)
5615db4cdaaStedu {
5625db4cdaaStedu 	return SipHash24(&lsacachekey, hdr, sizeof(*hdr));
5635db4cdaaStedu }
5645db4cdaaStedu 
565204df0f8Sclaudio struct lsa_ref *
566204df0f8Sclaudio lsa_cache_add(void *data, u_int16_t len)
567204df0f8Sclaudio {
568204df0f8Sclaudio 	struct lsa_cache_head	*head;
569204df0f8Sclaudio 	struct lsa_ref		*ref, *old;
570f896e954Snorby 	struct timespec		 tp;
571204df0f8Sclaudio 
572204df0f8Sclaudio 	if ((ref = calloc(1, sizeof(*ref))) == NULL)
573204df0f8Sclaudio 		fatal("lsa_cache_add");
574204df0f8Sclaudio 	memcpy(&ref->hdr, data, sizeof(ref->hdr));
575204df0f8Sclaudio 
576204df0f8Sclaudio 	if ((old = lsa_cache_look(&ref->hdr))) {
577204df0f8Sclaudio 		free(ref);
578204df0f8Sclaudio 		old->refcnt++;
579204df0f8Sclaudio 		return (old);
580204df0f8Sclaudio 	}
581204df0f8Sclaudio 
582204df0f8Sclaudio 	if ((ref->data = malloc(len)) == NULL)
583204df0f8Sclaudio 		fatal("lsa_cache_add");
584204df0f8Sclaudio 	memcpy(ref->data, data, len);
585f896e954Snorby 
586f896e954Snorby 	clock_gettime(CLOCK_MONOTONIC, &tp);
587f896e954Snorby 	ref->stamp = tp.tv_sec;
588204df0f8Sclaudio 	ref->len = len;
589204df0f8Sclaudio 	ref->refcnt = 1;
590204df0f8Sclaudio 
5915db4cdaaStedu 	head = &lsacache.hashtbl[lsa_hash_hdr(&ref->hdr) & lsacache.hashmask];
592204df0f8Sclaudio 	LIST_INSERT_HEAD(head, ref, entry);
593204df0f8Sclaudio 	return (ref);
594204df0f8Sclaudio }
595204df0f8Sclaudio 
596204df0f8Sclaudio struct lsa_ref *
597204df0f8Sclaudio lsa_cache_get(struct lsa_hdr *lsa_hdr)
598204df0f8Sclaudio {
599204df0f8Sclaudio 	struct lsa_ref		*ref;
600204df0f8Sclaudio 
601204df0f8Sclaudio 	ref = lsa_cache_look(lsa_hdr);
602204df0f8Sclaudio 	if (ref)
603204df0f8Sclaudio 		ref->refcnt++;
604204df0f8Sclaudio 
605204df0f8Sclaudio 	return (ref);
606204df0f8Sclaudio }
607204df0f8Sclaudio 
608204df0f8Sclaudio void
609204df0f8Sclaudio lsa_cache_put(struct lsa_ref *ref, struct nbr *nbr)
610204df0f8Sclaudio {
611204df0f8Sclaudio 	if (--ref->refcnt > 0)
612204df0f8Sclaudio 		return;
613204df0f8Sclaudio 
614204df0f8Sclaudio 	if (ntohs(ref->hdr.age) >= MAX_AGE)
615204df0f8Sclaudio 		ospfe_imsg_compose_rde(IMSG_LS_MAXAGE, nbr->peerid, 0,
616204df0f8Sclaudio 		    ref->data, sizeof(struct lsa_hdr));
617204df0f8Sclaudio 
618204df0f8Sclaudio 	free(ref->data);
619204df0f8Sclaudio 	LIST_REMOVE(ref, entry);
620204df0f8Sclaudio 	free(ref);
621204df0f8Sclaudio }
622204df0f8Sclaudio 
623204df0f8Sclaudio struct lsa_ref *
624204df0f8Sclaudio lsa_cache_look(struct lsa_hdr *lsa_hdr)
625204df0f8Sclaudio {
626204df0f8Sclaudio 	struct lsa_cache_head	*head;
627204df0f8Sclaudio 	struct lsa_ref		*ref;
628204df0f8Sclaudio 
6295db4cdaaStedu 	head = &lsacache.hashtbl[lsa_hash_hdr(lsa_hdr) & lsacache.hashmask];
63064896ccbSclaudio 
631204df0f8Sclaudio 	LIST_FOREACH(ref, head, entry) {
63264896ccbSclaudio 		if (memcmp(&ref->hdr, lsa_hdr, sizeof(*lsa_hdr)) == 0)
633204df0f8Sclaudio 			/* found match */
634204df0f8Sclaudio 			return (ref);
635204df0f8Sclaudio 	}
63664896ccbSclaudio 
637204df0f8Sclaudio 	return (NULL);
638204df0f8Sclaudio }
639