xref: /openbsd-src/usr.sbin/ospfd/hello.c (revision 4f4fe40bc9d06de09f4af934b87299ab1ee3cc66)
1*4f4fe40bSflorian /*	$OpenBSD: hello.c,v 1.27 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 #include <sys/time.h>
25204df0f8Sclaudio #include <stdlib.h>
26204df0f8Sclaudio #include <string.h>
27204df0f8Sclaudio #include <event.h>
28204df0f8Sclaudio 
29204df0f8Sclaudio #include "ospfd.h"
30204df0f8Sclaudio #include "ospf.h"
31204df0f8Sclaudio #include "log.h"
32204df0f8Sclaudio #include "ospfe.h"
33204df0f8Sclaudio 
34204df0f8Sclaudio extern struct ospfd_conf	*oeconf;
35204df0f8Sclaudio 
36204df0f8Sclaudio /* hello packet handling */
37204df0f8Sclaudio int
38204df0f8Sclaudio send_hello(struct iface *iface)
39204df0f8Sclaudio {
40204df0f8Sclaudio 	struct sockaddr_in	 dst;
410491ce75Sclaudio 	struct hello_hdr	 hello;
42204df0f8Sclaudio 	struct nbr		*nbr;
43e39620e5Snicm 	struct ibuf		*buf;
44204df0f8Sclaudio 
45204df0f8Sclaudio 	dst.sin_family = AF_INET;
46204df0f8Sclaudio 	dst.sin_len = sizeof(struct sockaddr_in);
47204df0f8Sclaudio 
48204df0f8Sclaudio 	switch (iface->type) {
49204df0f8Sclaudio 	case IF_TYPE_POINTOPOINT:
50204df0f8Sclaudio 	case IF_TYPE_BROADCAST:
51*4f4fe40bSflorian 		inet_pton(AF_INET, AllSPFRouters, &dst.sin_addr);
52204df0f8Sclaudio 		break;
53204df0f8Sclaudio 	case IF_TYPE_NBMA:
54204df0f8Sclaudio 	case IF_TYPE_POINTOMULTIPOINT:
550b593adcSclaudio 		log_debug("send_hello: type %s not supported, interface %s",
560b593adcSclaudio 		    if_type_name(iface->type), iface->name);
570b593adcSclaudio 		return (-1);
58204df0f8Sclaudio 	case IF_TYPE_VIRTUALLINK:
59ebc9e90aSnorby 		dst.sin_addr = iface->dst;
60204df0f8Sclaudio 		break;
61204df0f8Sclaudio 	default:
62204df0f8Sclaudio 		fatalx("send_hello: unknown interface type");
63204df0f8Sclaudio 	}
64204df0f8Sclaudio 
656860b38aSclaudio 	if ((buf = ibuf_dynamic(PKG_DEF_SIZE,
666860b38aSclaudio 	    IP_MAXPACKET - sizeof(struct ip))) == NULL)
670b593adcSclaudio 		fatal("send_hello");
680b593adcSclaudio 
69204df0f8Sclaudio 	/* OSPF header */
700491ce75Sclaudio 	if (gen_ospf_hdr(buf, iface, PACKET_TYPE_HELLO))
710491ce75Sclaudio 		goto fail;
72204df0f8Sclaudio 
73204df0f8Sclaudio 	/* hello header */
740491ce75Sclaudio 	hello.mask = iface->mask.s_addr;
750491ce75Sclaudio 	hello.hello_interval = htons(iface->hello_interval);
763ffd5c19Sclaudio 	hello.opts = area_ospf_options(iface->area);
770491ce75Sclaudio 	hello.rtr_priority = iface->priority;
780491ce75Sclaudio 	hello.rtr_dead_interval = htonl(iface->dead_interval);
79204df0f8Sclaudio 
80ed5b76e9Sclaudio 	if (iface->dr) {
810491ce75Sclaudio 		hello.d_rtr = iface->dr->addr.s_addr;
82ed5b76e9Sclaudio 		iface->self->dr.s_addr = iface->dr->addr.s_addr;
830491ce75Sclaudio 	} else
840491ce75Sclaudio 		hello.d_rtr = 0;
85ed5b76e9Sclaudio 	if (iface->bdr) {
860491ce75Sclaudio 		hello.bd_rtr = iface->bdr->addr.s_addr;
87ed5b76e9Sclaudio 		iface->self->bdr.s_addr = iface->bdr->addr.s_addr;
880491ce75Sclaudio 	} else
890491ce75Sclaudio 		hello.bd_rtr = 0;
900491ce75Sclaudio 
91e39620e5Snicm 	if (ibuf_add(buf, &hello, sizeof(hello)))
920491ce75Sclaudio 		goto fail;
93204df0f8Sclaudio 
94204df0f8Sclaudio 	/* active neighbor(s) */
95204df0f8Sclaudio 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
960491ce75Sclaudio 		if ((nbr->state >= NBR_STA_INIT) && (nbr != iface->self))
97e39620e5Snicm 			if (ibuf_add(buf, &nbr->id, sizeof(nbr->id)))
980491ce75Sclaudio 				goto fail;
99204df0f8Sclaudio 	}
100204df0f8Sclaudio 
101204df0f8Sclaudio 	/* update authentication and calculate checksum */
1020491ce75Sclaudio 	if (auth_gen(buf, iface))
1030491ce75Sclaudio 		goto fail;
104204df0f8Sclaudio 
10525a742ccSremi 	if (send_packet(iface, buf, &dst) == -1)
10625a742ccSremi 		goto fail;
10725a742ccSremi 
108e39620e5Snicm 	ibuf_free(buf);
10925a742ccSremi 	return (0);
1100491ce75Sclaudio fail:
11125a742ccSremi 	log_warn("%s", __func__);
112e39620e5Snicm 	ibuf_free(buf);
1130491ce75Sclaudio 	return (-1);
114204df0f8Sclaudio }
115204df0f8Sclaudio 
116204df0f8Sclaudio void
117204df0f8Sclaudio recv_hello(struct iface *iface, struct in_addr src, u_int32_t rtr_id, char *buf,
118204df0f8Sclaudio     u_int16_t len)
119204df0f8Sclaudio {
120204df0f8Sclaudio 	struct hello_hdr	 hello;
121e9496ee4Sclaudio 	struct nbr		*nbr = NULL, *dr;
122204df0f8Sclaudio 	u_int32_t		 nbr_id;
123e9496ee4Sclaudio 	int			 nbr_change = 0;
124204df0f8Sclaudio 
12565b16aecSjacekm 	if (len < sizeof(hello) || (len & 0x03)) {
126204df0f8Sclaudio 		log_warnx("recv_hello: bad packet size, interface %s",
127204df0f8Sclaudio 		    iface->name);
128204df0f8Sclaudio 		return;
129204df0f8Sclaudio 	}
130204df0f8Sclaudio 
131204df0f8Sclaudio 	memcpy(&hello, buf, sizeof(hello));
132204df0f8Sclaudio 	buf += sizeof(hello);
133204df0f8Sclaudio 	len -= sizeof(hello);
134204df0f8Sclaudio 
135204df0f8Sclaudio 	if (iface->type != IF_TYPE_POINTOPOINT &&
136204df0f8Sclaudio 	    iface->type != IF_TYPE_VIRTUALLINK)
137204df0f8Sclaudio 		if (hello.mask != iface->mask.s_addr) {
138204df0f8Sclaudio 			log_warnx("recv_hello: invalid netmask, interface %s",
139204df0f8Sclaudio 			    iface->name);
140204df0f8Sclaudio 			return;
141204df0f8Sclaudio 		}
142204df0f8Sclaudio 
143204df0f8Sclaudio 	if (ntohs(hello.hello_interval) != iface->hello_interval) {
144204df0f8Sclaudio 		log_warnx("recv_hello: invalid hello-interval %d, "
145204df0f8Sclaudio 		    "interface %s", ntohs(hello.hello_interval),
146204df0f8Sclaudio 		    iface->name);
147204df0f8Sclaudio 		return;
148204df0f8Sclaudio 	}
149204df0f8Sclaudio 
150204df0f8Sclaudio 	if (ntohl(hello.rtr_dead_interval) != iface->dead_interval) {
151204df0f8Sclaudio 		log_warnx("recv_hello: invalid router-dead-interval %d, "
152204df0f8Sclaudio 		    "interface %s", ntohl(hello.rtr_dead_interval),
153204df0f8Sclaudio 		    iface->name);
154204df0f8Sclaudio 		return;
155204df0f8Sclaudio 	}
156204df0f8Sclaudio 
157204df0f8Sclaudio 	if ((hello.opts & OSPF_OPTION_E && iface->area->stub) ||
158204df0f8Sclaudio 	    ((hello.opts & OSPF_OPTION_E) == 0 && !iface->area->stub)) {
159204df0f8Sclaudio 		log_warnx("recv_hello: ExternalRoutingCapability mismatch, "
160204df0f8Sclaudio 		    "interface %s", iface->name);
161204df0f8Sclaudio 		return;
162204df0f8Sclaudio 	}
163204df0f8Sclaudio 
164aaa979c9Sclaudio 	/*
165aaa979c9Sclaudio 	 * Match router-id, in case of conflict moan and ignore hello.
166aaa979c9Sclaudio 	 * Only the router-id is compared since the source IP on NBMA,
167aaa979c9Sclaudio 	 * broadcast and point-to-multipoint interfaces was already
168aaa979c9Sclaudio 	 * compared in find_iface() and only IPs in the same subnet
1693a50f0a9Sjmc 	 * are accepted. This is not exactly what the RFC specifies
170aaa979c9Sclaudio 	 * but works far better.
171aaa979c9Sclaudio 	 */
172204df0f8Sclaudio 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
173aaa979c9Sclaudio 		if (nbr == iface->self) {
174aaa979c9Sclaudio 			if (nbr->id.s_addr == rtr_id) {
1757cfdfd15Ssthen 				log_warnx("recv_hello: Router-ID collision on "
176aaa979c9Sclaudio 				    "interface %s neighbor IP %s", iface->name,
177aaa979c9Sclaudio 				    inet_ntoa(src));
178aaa979c9Sclaudio 				return;
179aaa979c9Sclaudio 			}
180204df0f8Sclaudio 			continue;
181aaa979c9Sclaudio 		}
182204df0f8Sclaudio 		if (nbr->id.s_addr == rtr_id)
183204df0f8Sclaudio 			break;
184204df0f8Sclaudio 	}
185204df0f8Sclaudio 
186e9496ee4Sclaudio 	if (!nbr) {
187204df0f8Sclaudio 		nbr = nbr_new(rtr_id, iface, 0);
188e9496ee4Sclaudio 		/* set neighbor parameters */
189e9496ee4Sclaudio 		nbr->dr.s_addr = hello.d_rtr;
190e9496ee4Sclaudio 		nbr->bdr.s_addr = hello.bd_rtr;
191e9496ee4Sclaudio 		nbr->priority = hello.rtr_priority;
1922fa37528Sremi 		/* XXX neighbor address shouldn't be stored on virtual links */
1932fa37528Sremi 		nbr->addr.s_addr = src.s_addr;
194ef209401Sremi 		ospfe_imsg_compose_rde(IMSG_NEIGHBOR_ADDR, nbr->peerid, 0,
195ef209401Sremi 		    &src, sizeof(src));
196e9496ee4Sclaudio 	}
197ebc9e90aSnorby 
1982fa37528Sremi 	if (nbr->addr.s_addr != src.s_addr) {
1992fa37528Sremi 		log_warnx("%s: neighbor ID %s changed its IP address",
2002fa37528Sremi 		    __func__, inet_ntoa(nbr->id));
201204df0f8Sclaudio 		nbr->addr.s_addr = src.s_addr;
202ef209401Sremi 		ospfe_imsg_compose_rde(IMSG_NEIGHBOR_ADDR, nbr->peerid, 0,
203ef209401Sremi 		    &src, sizeof(src));
2042fa37528Sremi 	}
2052fa37528Sremi 
206204df0f8Sclaudio 	nbr->options = hello.opts;
207204df0f8Sclaudio 
208204df0f8Sclaudio 	nbr_fsm(nbr, NBR_EVT_HELLO_RCVD);
209204df0f8Sclaudio 
210204df0f8Sclaudio 	while (len >= sizeof(nbr_id)) {
211204df0f8Sclaudio 		memcpy(&nbr_id, buf, sizeof(nbr_id));
212f12637e5Smsf 		if (nbr_id == ospfe_router_id()) {
213204df0f8Sclaudio 			/* seen myself */
214bd0b23eeSclaudio 			if (nbr->state & NBR_STA_PRELIM) {
215e9496ee4Sclaudio 				nbr_fsm(nbr, NBR_EVT_2_WAY_RCVD);
216bd0b23eeSclaudio 				nbr_change = 1;
217bd0b23eeSclaudio 			}
218204df0f8Sclaudio 			break;
219204df0f8Sclaudio 		}
220204df0f8Sclaudio 		buf += sizeof(nbr_id);
221204df0f8Sclaudio 		len -= sizeof(nbr_id);
222204df0f8Sclaudio 	}
223204df0f8Sclaudio 
224204df0f8Sclaudio 	if (len == 0) {
225204df0f8Sclaudio 		nbr_fsm(nbr, NBR_EVT_1_WAY_RCVD);
226204df0f8Sclaudio 		/* set neighbor parameters */
227204df0f8Sclaudio 		nbr->dr.s_addr = hello.d_rtr;
228204df0f8Sclaudio 		nbr->bdr.s_addr = hello.bd_rtr;
229204df0f8Sclaudio 		nbr->priority = hello.rtr_priority;
230204df0f8Sclaudio 		return;
231204df0f8Sclaudio 	}
232204df0f8Sclaudio 
233204df0f8Sclaudio 	if (nbr->priority != hello.rtr_priority) {
234204df0f8Sclaudio 		nbr->priority = hello.rtr_priority;
235204df0f8Sclaudio 		nbr_change = 1;
236204df0f8Sclaudio 	}
237204df0f8Sclaudio 
2385ec720d9Sclaudio 	if (iface->state & IF_STA_WAITING &&
239581e0255Sclaudio 	    hello.d_rtr == nbr->addr.s_addr && hello.bd_rtr == 0)
240204df0f8Sclaudio 		if_fsm(iface, IF_EVT_BACKUP_SEEN);
241e9496ee4Sclaudio 
242e9496ee4Sclaudio 	if (iface->state & IF_STA_WAITING && hello.bd_rtr == nbr->addr.s_addr) {
243e9496ee4Sclaudio 		/*
244e9496ee4Sclaudio 		 * In case we see the BDR make sure that the DR is around
245e9496ee4Sclaudio 		 * with a bidirectional (2_WAY or better) connection
246e9496ee4Sclaudio 		 */
247e9496ee4Sclaudio 		LIST_FOREACH(dr, &iface->nbr_list, entry)
248e9496ee4Sclaudio 			if (hello.d_rtr == dr->addr.s_addr &&
249581e0255Sclaudio 			    dr->state & NBR_STA_BIDIR)
250e9496ee4Sclaudio 				if_fsm(iface, IF_EVT_BACKUP_SEEN);
251e9496ee4Sclaudio 	}
252204df0f8Sclaudio 
253204df0f8Sclaudio 	if ((nbr->addr.s_addr == nbr->dr.s_addr &&
254204df0f8Sclaudio 	    nbr->addr.s_addr != hello.d_rtr) ||
255204df0f8Sclaudio 	    (nbr->addr.s_addr != nbr->dr.s_addr &&
256204df0f8Sclaudio 	    nbr->addr.s_addr == hello.d_rtr))
257204df0f8Sclaudio 		/* neighbor changed from or to DR */
258204df0f8Sclaudio 		nbr_change = 1;
259204df0f8Sclaudio 	if ((nbr->addr.s_addr == nbr->bdr.s_addr &&
260204df0f8Sclaudio 	    nbr->addr.s_addr != hello.bd_rtr) ||
261204df0f8Sclaudio 	    (nbr->addr.s_addr != nbr->bdr.s_addr &&
262204df0f8Sclaudio 	    nbr->addr.s_addr == hello.bd_rtr))
263204df0f8Sclaudio 		/* neighbor changed from or to BDR */
264204df0f8Sclaudio 		nbr_change = 1;
265204df0f8Sclaudio 
266204df0f8Sclaudio 	nbr->dr.s_addr = hello.d_rtr;
267204df0f8Sclaudio 	nbr->bdr.s_addr = hello.bd_rtr;
268204df0f8Sclaudio 
269204df0f8Sclaudio 	if (nbr_change)
270204df0f8Sclaudio 		if_fsm(iface, IF_EVT_NBR_CHNG);
271204df0f8Sclaudio 
272204df0f8Sclaudio 	/* TODO NBMA needs some special handling */
273204df0f8Sclaudio }
274