xref: /openbsd-src/usr.sbin/ospfd/lsack.c (revision 4f4fe40bc9d06de09f4af934b87299ab1ee3cc66)
1*4f4fe40bSflorian /*	$OpenBSD: lsack.c,v 1.25 2024/08/21 15:18:00 florian Exp $ */
2204df0f8Sclaudio 
3204df0f8Sclaudio /*
4367f601bSnorby  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
5204df0f8Sclaudio  *
6204df0f8Sclaudio  * Permission to use, copy, modify, and distribute this software for any
7204df0f8Sclaudio  * purpose with or without fee is hereby granted, provided that the above
8204df0f8Sclaudio  * copyright notice and this permission notice appear in all copies.
9204df0f8Sclaudio  *
10204df0f8Sclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11204df0f8Sclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12204df0f8Sclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13204df0f8Sclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14204df0f8Sclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15204df0f8Sclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16204df0f8Sclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17204df0f8Sclaudio  */
18204df0f8Sclaudio 
19204df0f8Sclaudio #include <sys/types.h>
20204df0f8Sclaudio #include <sys/socket.h>
21204df0f8Sclaudio #include <netinet/in.h>
22204df0f8Sclaudio #include <netinet/ip.h>
23204df0f8Sclaudio #include <arpa/inet.h>
24204df0f8Sclaudio 
25204df0f8Sclaudio #include <stdlib.h>
2653e1908aSstevesk #include <string.h>
27204df0f8Sclaudio 
28204df0f8Sclaudio #include "ospfd.h"
29204df0f8Sclaudio #include "ospf.h"
30204df0f8Sclaudio #include "log.h"
31204df0f8Sclaudio #include "ospfe.h"
32204df0f8Sclaudio 
3362d04914Sclaudio int		 send_ls_ack(struct iface *, struct in_addr, struct ibuf *);
3462d04914Sclaudio struct ibuf	*prepare_ls_ack(struct iface *);
353aede346Sclaudio void		 start_ls_ack_tx_timer_now(struct iface *);
36204df0f8Sclaudio 
37204df0f8Sclaudio /* link state acknowledgement packet handling */
3862d04914Sclaudio struct ibuf *
3962d04914Sclaudio prepare_ls_ack(struct iface *iface)
4062d04914Sclaudio {
4162d04914Sclaudio 	struct ibuf	*buf;
4262d04914Sclaudio 
4362d04914Sclaudio 	if ((buf = ibuf_open(iface->mtu - sizeof(struct ip))) == NULL) {
4462d04914Sclaudio 		log_warn("prepare_ls_ack");
4562d04914Sclaudio 		return (NULL);
4662d04914Sclaudio 	}
4762d04914Sclaudio 
4862d04914Sclaudio 	/* OSPF header */
4962d04914Sclaudio 	if (gen_ospf_hdr(buf, iface, PACKET_TYPE_LS_ACK)) {
5062d04914Sclaudio 		log_warn("prepare_ls_ack");
5162d04914Sclaudio 		ibuf_free(buf);
5262d04914Sclaudio 		return (NULL);
5362d04914Sclaudio 	}
5462d04914Sclaudio 
5562d04914Sclaudio 	return (buf);
5662d04914Sclaudio }
5762d04914Sclaudio 
58204df0f8Sclaudio int
5962d04914Sclaudio send_ls_ack(struct iface *iface, struct in_addr addr, struct ibuf *buf)
60204df0f8Sclaudio {
61204df0f8Sclaudio 	struct sockaddr_in	dst;
62204df0f8Sclaudio 
6362d04914Sclaudio 	/* update authentication and calculate checksum */
6462d04914Sclaudio 	if (auth_gen(buf, iface)) {
6562d04914Sclaudio 		log_warn("send_ls_ack");
6662d04914Sclaudio 		return (-1);
6762d04914Sclaudio 	}
68204df0f8Sclaudio 
69204df0f8Sclaudio 	dst.sin_family = AF_INET;
70204df0f8Sclaudio 	dst.sin_len = sizeof(struct sockaddr_in);
71204df0f8Sclaudio 	dst.sin_addr.s_addr = addr.s_addr;
72204df0f8Sclaudio 
7325a742ccSremi 	if (send_packet(iface, buf, &dst) == -1) {
7425a742ccSremi 		log_warn("%s", __func__);
7525a742ccSremi 		return (-1);
7625a742ccSremi 	}
7725a742ccSremi 	return (0);
7862d04914Sclaudio }
7962d04914Sclaudio 
8062d04914Sclaudio int
8162d04914Sclaudio send_direct_ack(struct iface *iface, struct in_addr addr, void *d, size_t len)
8262d04914Sclaudio {
8362d04914Sclaudio 	struct ibuf	*buf;
8462d04914Sclaudio 	int		 ret;
8562d04914Sclaudio 
8662d04914Sclaudio 	if ((buf = prepare_ls_ack(iface)) == NULL)
8762d04914Sclaudio 		return (-1);
88204df0f8Sclaudio 
89204df0f8Sclaudio 	/* LS ack(s) */
9062d04914Sclaudio 	if (ibuf_add(buf, d, len)) {
9162d04914Sclaudio 		log_warn("send_direct_ack");
92e39620e5Snicm 		ibuf_free(buf);
930491ce75Sclaudio 		return (-1);
94204df0f8Sclaudio 	}
95204df0f8Sclaudio 
9662d04914Sclaudio 	ret = send_ls_ack(iface, addr, buf);
9762d04914Sclaudio 	ibuf_free(buf);
9862d04914Sclaudio 	return (ret);
9962d04914Sclaudio }
10062d04914Sclaudio 
101204df0f8Sclaudio void
102204df0f8Sclaudio recv_ls_ack(struct nbr *nbr, char *buf, u_int16_t len)
103204df0f8Sclaudio {
104204df0f8Sclaudio 	struct lsa_hdr	 lsa_hdr;
105204df0f8Sclaudio 
106204df0f8Sclaudio 	switch (nbr->state) {
107204df0f8Sclaudio 	case NBR_STA_DOWN:
108204df0f8Sclaudio 	case NBR_STA_ATTEMPT:
109204df0f8Sclaudio 	case NBR_STA_INIT:
110204df0f8Sclaudio 	case NBR_STA_2_WAY:
111204df0f8Sclaudio 	case NBR_STA_XSTRT:
112204df0f8Sclaudio 	case NBR_STA_SNAP:
113204df0f8Sclaudio 		log_debug("recv_ls_ack: packet ignored in state %s, "
1149cc19ef1Ssthen 		    "neighbor ID %s (%s)", nbr_state_name(nbr->state),
1159cc19ef1Ssthen 		    inet_ntoa(nbr->id), nbr->iface->name);
116204df0f8Sclaudio 		break;
117204df0f8Sclaudio 	case NBR_STA_XCHNG:
118204df0f8Sclaudio 	case NBR_STA_LOAD:
119204df0f8Sclaudio 	case NBR_STA_FULL:
120204df0f8Sclaudio 		while (len >= sizeof(lsa_hdr)) {
121204df0f8Sclaudio 			memcpy(&lsa_hdr, buf, sizeof(lsa_hdr));
122204df0f8Sclaudio 
123cb75af8bSclaudio 			if (lsa_hdr_check(nbr, &lsa_hdr)) {
124cb75af8bSclaudio 				/* try both list in case of DROTHER */
125cb75af8bSclaudio 				if (nbr->iface->state & IF_STA_DROTHER)
1263aede346Sclaudio 					(void)ls_retrans_list_del(
1273aede346Sclaudio 					    nbr->iface->self, &lsa_hdr);
1283aede346Sclaudio 				(void)ls_retrans_list_del(nbr, &lsa_hdr);
129cb75af8bSclaudio 			}
130204df0f8Sclaudio 
131204df0f8Sclaudio 			buf += sizeof(lsa_hdr);
132204df0f8Sclaudio 			len -= sizeof(lsa_hdr);
133204df0f8Sclaudio 		}
134204df0f8Sclaudio 		if (len > 0) {
135204df0f8Sclaudio 			log_warnx("recv_ls_ack: bad packet size, "
1369cc19ef1Ssthen 			    "neighbor ID %s (%s)", inet_ntoa(nbr->id),
1379cc19ef1Ssthen 			    nbr->iface->name);
138204df0f8Sclaudio 			return;
139204df0f8Sclaudio 		}
140204df0f8Sclaudio 		break;
141204df0f8Sclaudio 	default:
142204df0f8Sclaudio 		fatalx("recv_ls_ack: unknown neighbor state");
143204df0f8Sclaudio 	}
144204df0f8Sclaudio }
145204df0f8Sclaudio 
146204df0f8Sclaudio int
147204df0f8Sclaudio lsa_hdr_check(struct nbr *nbr, struct lsa_hdr *lsa_hdr)
148204df0f8Sclaudio {
149204df0f8Sclaudio 	/* invalid age */
150204df0f8Sclaudio 	if ((ntohs(lsa_hdr->age) < 1) || (ntohs(lsa_hdr->age) > MAX_AGE)) {
1519cc19ef1Ssthen 		log_debug("lsa_hdr_check: invalid age, neighbor ID %s (%s)",
1529cc19ef1Ssthen 		     inet_ntoa(nbr->id), nbr->iface->name);
153204df0f8Sclaudio 		return (0);
154204df0f8Sclaudio 	}
155204df0f8Sclaudio 
156204df0f8Sclaudio 	/* invalid type */
157204df0f8Sclaudio 	switch (lsa_hdr->type) {
158204df0f8Sclaudio 	case LSA_TYPE_ROUTER:
159204df0f8Sclaudio 	case LSA_TYPE_NETWORK:
160204df0f8Sclaudio 	case LSA_TYPE_SUM_NETWORK:
161204df0f8Sclaudio 	case LSA_TYPE_SUM_ROUTER:
162204df0f8Sclaudio 	case LSA_TYPE_EXTERNAL:
163204df0f8Sclaudio 		break;
164204df0f8Sclaudio 	default:
1659cc19ef1Ssthen 		log_debug("lsa_hdr_check: invalid LSA type %d, "
1669cc19ef1Ssthen 		    "neighbor ID %s (%s)",
1679cc19ef1Ssthen 		    lsa_hdr->type, inet_ntoa(nbr->id), nbr->iface->name);
168204df0f8Sclaudio 		return (0);
169204df0f8Sclaudio 	}
170204df0f8Sclaudio 
171204df0f8Sclaudio 	/* invalid sequence number */
172fd136cabSclaudio 	if (ntohl(lsa_hdr->seq_num) == RESV_SEQ_NUM) {
1739cc19ef1Ssthen 		log_debug("ls_hdr_check: invalid seq num, "
1749cc19ef1Ssthen 		    "neighbor ID %s (%s)", inet_ntoa(nbr->id),
1759cc19ef1Ssthen 		    nbr->iface->name);
176204df0f8Sclaudio 		return (0);
177204df0f8Sclaudio 	}
178204df0f8Sclaudio 
179204df0f8Sclaudio 	return (1);
180204df0f8Sclaudio }
181204df0f8Sclaudio 
182204df0f8Sclaudio /* link state ack list */
183204df0f8Sclaudio void
184204df0f8Sclaudio ls_ack_list_add(struct iface *iface, struct lsa_hdr *lsa)
185204df0f8Sclaudio {
186204df0f8Sclaudio 	struct lsa_entry	*le;
187204df0f8Sclaudio 
188204df0f8Sclaudio 	if (lsa == NULL)
189204df0f8Sclaudio 		fatalx("ls_ack_list_add: no LSA header");
190204df0f8Sclaudio 
191204df0f8Sclaudio 	if ((le = calloc(1, sizeof(*le))) == NULL)
192204df0f8Sclaudio 		fatal("ls_ack_list_add");
193204df0f8Sclaudio 
194204df0f8Sclaudio 	if (ls_ack_list_empty(iface))
195204df0f8Sclaudio 		start_ls_ack_tx_timer(iface);
196204df0f8Sclaudio 
197204df0f8Sclaudio 	TAILQ_INSERT_TAIL(&iface->ls_ack_list, le, entry);
198204df0f8Sclaudio 	le->le_lsa = lsa;
199204df0f8Sclaudio 	iface->ls_ack_cnt++;
200204df0f8Sclaudio 
20162d04914Sclaudio 	/* reschedule now if we have enough for a reasonably sized packet */
20262d04914Sclaudio 	if (iface->ls_ack_cnt > IP_MSS / sizeof(struct lsa_hdr))
203204df0f8Sclaudio 		start_ls_ack_tx_timer_now(iface);
204204df0f8Sclaudio }
205204df0f8Sclaudio 
206204df0f8Sclaudio void
207204df0f8Sclaudio ls_ack_list_free(struct iface *iface, struct lsa_entry *le)
208204df0f8Sclaudio {
209204df0f8Sclaudio 	TAILQ_REMOVE(&iface->ls_ack_list, le, entry);
210204df0f8Sclaudio 	free(le->le_lsa);
211204df0f8Sclaudio 	free(le);
212204df0f8Sclaudio 
213204df0f8Sclaudio 	iface->ls_ack_cnt--;
214204df0f8Sclaudio }
215204df0f8Sclaudio 
216204df0f8Sclaudio void
217204df0f8Sclaudio ls_ack_list_clr(struct iface *iface)
218204df0f8Sclaudio {
219204df0f8Sclaudio 	struct lsa_entry	*le;
220204df0f8Sclaudio 
221204df0f8Sclaudio 	while ((le = TAILQ_FIRST(&iface->ls_ack_list)) != NULL) {
222204df0f8Sclaudio 		TAILQ_REMOVE(&iface->ls_ack_list, le, entry);
223204df0f8Sclaudio 		free(le->le_lsa);
224204df0f8Sclaudio 		free(le);
225204df0f8Sclaudio 	}
226204df0f8Sclaudio 	iface->ls_ack_cnt = 0;
227204df0f8Sclaudio }
228204df0f8Sclaudio 
2295c6e55e9Snorby int
230204df0f8Sclaudio ls_ack_list_empty(struct iface *iface)
231204df0f8Sclaudio {
232204df0f8Sclaudio 	return (TAILQ_EMPTY(&iface->ls_ack_list));
233204df0f8Sclaudio }
234204df0f8Sclaudio 
235204df0f8Sclaudio /* timers */
236204df0f8Sclaudio void
237204df0f8Sclaudio ls_ack_tx_timer(int fd, short event, void *arg)
238204df0f8Sclaudio {
239204df0f8Sclaudio 	struct in_addr		 addr;
240204df0f8Sclaudio 	struct iface		*iface = arg;
241204df0f8Sclaudio 	struct lsa_entry	*le, *nle;
242204df0f8Sclaudio 	struct nbr		*nbr;
24362d04914Sclaudio 	struct ibuf		*buf;
24462d04914Sclaudio 	int			 cnt;
245204df0f8Sclaudio 
246204df0f8Sclaudio 	while (!ls_ack_list_empty(iface)) {
24762d04914Sclaudio 		if ((buf = prepare_ls_ack(iface)) == NULL)
24862d04914Sclaudio 			fatal("ls_ack_tx_timer");
249204df0f8Sclaudio 		cnt = 0;
25062d04914Sclaudio 
25162d04914Sclaudio 		for (le = TAILQ_FIRST(&iface->ls_ack_list); le != NULL;
25262d04914Sclaudio 		    le = nle) {
253204df0f8Sclaudio 			nle = TAILQ_NEXT(le, entry);
25462d04914Sclaudio 			if (ibuf_left(buf) < sizeof(struct lsa_hdr) +
25562d04914Sclaudio 			    MD5_DIGEST_LENGTH)
25662d04914Sclaudio 				break;
25762d04914Sclaudio 			if (ibuf_add(buf, le->le_lsa, sizeof(struct lsa_hdr)))
25862d04914Sclaudio 				break;
259204df0f8Sclaudio 			ls_ack_list_free(iface, le);
260204df0f8Sclaudio 			cnt++;
261204df0f8Sclaudio 		}
26262d04914Sclaudio 		if (cnt == 0) {
26362d04914Sclaudio 			log_warnx("ls_ack_tx_timer: lost in space");
26462d04914Sclaudio 			ibuf_free(buf);
26562d04914Sclaudio 			return;
26662d04914Sclaudio 		}
267204df0f8Sclaudio 
268204df0f8Sclaudio 		/* send LS ack(s) but first set correct destination */
269204df0f8Sclaudio 		switch (iface->type) {
270204df0f8Sclaudio 		case IF_TYPE_POINTOPOINT:
271*4f4fe40bSflorian 			inet_pton(AF_INET, AllSPFRouters, &addr);
27262d04914Sclaudio 			send_ls_ack(iface, addr, buf);
273204df0f8Sclaudio 			break;
274204df0f8Sclaudio 		case IF_TYPE_BROADCAST:
275204df0f8Sclaudio 			if (iface->state & IF_STA_DRORBDR)
276*4f4fe40bSflorian 				inet_pton(AF_INET, AllSPFRouters, &addr);
277204df0f8Sclaudio 			else
278*4f4fe40bSflorian 				inet_pton(AF_INET, AllDRouters, &addr);
27962d04914Sclaudio 			send_ls_ack(iface, addr, buf);
280204df0f8Sclaudio 			break;
281204df0f8Sclaudio 		case IF_TYPE_NBMA:
282204df0f8Sclaudio 		case IF_TYPE_POINTOMULTIPOINT:
283204df0f8Sclaudio 		case IF_TYPE_VIRTUALLINK:
284204df0f8Sclaudio 			LIST_FOREACH(nbr, &iface->nbr_list, entry) {
285204df0f8Sclaudio 				if (nbr == iface->self)
286204df0f8Sclaudio 					continue;
287204df0f8Sclaudio 				if (!(nbr->state & NBR_STA_FLOOD))
288204df0f8Sclaudio 					continue;
28962d04914Sclaudio 				send_ls_ack(iface, nbr->addr, buf);
290204df0f8Sclaudio 			}
291204df0f8Sclaudio 			break;
292204df0f8Sclaudio 		default:
293db06c020Snorby 			fatalx("lsa_ack_tx_timer: unknown interface type");
294204df0f8Sclaudio 		}
29562d04914Sclaudio 		ibuf_free(buf);
296204df0f8Sclaudio 	}
297204df0f8Sclaudio }
298204df0f8Sclaudio 
2993aede346Sclaudio void
300204df0f8Sclaudio start_ls_ack_tx_timer(struct iface *iface)
301204df0f8Sclaudio {
302204df0f8Sclaudio 	struct timeval tv;
303204df0f8Sclaudio 
304204df0f8Sclaudio 	timerclear(&tv);
305204df0f8Sclaudio 	tv.tv_sec = iface->rxmt_interval / 2;
3064fafafe9Snorby 
3073aede346Sclaudio 	if (evtimer_add(&iface->lsack_tx_timer, &tv) == -1)
3083aede346Sclaudio 		fatal("start_ls_ack_tx_timer");
309204df0f8Sclaudio }
310204df0f8Sclaudio 
3113aede346Sclaudio void
312204df0f8Sclaudio start_ls_ack_tx_timer_now(struct iface *iface)
313204df0f8Sclaudio {
314204df0f8Sclaudio 	struct timeval tv;
315204df0f8Sclaudio 
316204df0f8Sclaudio 	timerclear(&tv);
3173aede346Sclaudio 	if (evtimer_add(&iface->lsack_tx_timer, &tv) == -1)
3183aede346Sclaudio 		fatal("start_ls_ack_tx_timer_now");
319204df0f8Sclaudio }
320204df0f8Sclaudio 
3213aede346Sclaudio void
322204df0f8Sclaudio stop_ls_ack_tx_timer(struct iface *iface)
323204df0f8Sclaudio {
3243aede346Sclaudio 	if (evtimer_del(&iface->lsack_tx_timer) == -1)
3253aede346Sclaudio 		fatal("stop_ls_ack_tx_timer");
326204df0f8Sclaudio }
327