xref: /openbsd/usr.sbin/ripd/neighbor.c (revision 5b133f3f)
1 /*	$OpenBSD: neighbor.c,v 1.12 2023/03/08 04:43:14 guenther Exp $ */
2 
3 /*
4  * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
5  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
6  * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/types.h>
22 #include <sys/ioctl.h>
23 #include <sys/time.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <net/if.h>
28 
29 #include <ctype.h>
30 #include <err.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <event.h>
35 
36 #include "ripd.h"
37 #include "rip.h"
38 #include "ripe.h"
39 #include "log.h"
40 #include "rde.h"
41 
42 void	nbr_set_timer(struct nbr *);
43 void	nbr_stop_timer(struct nbr *);
44 
45 void	nbr_failed_new(struct nbr *);
46 void	nbr_failed_timeout(int, short, void *);
47 
48 LIST_HEAD(nbr_head, nbr);
49 
50 struct nbr_table {
51 	struct nbr_head		*hashtbl;
52 	u_int32_t		 hashmask;
53 } nbrtable;
54 
55 #define NBR_HASH(x)		\
56 	&nbrtable.hashtbl[(x) & nbrtable.hashmask]
57 
58 u_int32_t	peercnt = NBR_CNTSTART;
59 
60 struct {
61 	int		state;
62 	enum nbr_event	event;
63 	enum nbr_action	action;
64 	int		new_state;
65 } nbr_fsm_tbl[] = {
66     /* current state	event that happened	action to take		resulting state */
67     {NBR_STA_DOWN,	NBR_EVT_REQUEST_RCVD,	NBR_ACT_NOTHING,	NBR_STA_REQ_RCVD},
68     {NBR_STA_DOWN,	NBR_EVT_RESPONSE_RCVD,	NBR_ACT_STRT_TIMER,	NBR_STA_ACTIVE},
69     {NBR_STA_ACTIVE,	NBR_EVT_RESPONSE_RCVD,	NBR_ACT_RST_TIMER,	NBR_STA_ACTIVE},
70     {NBR_STA_ACTIVE,	NBR_EVT_REQUEST_RCVD,	NBR_ACT_NOTHING,	NBR_STA_ACTIVE},
71     {NBR_STA_ACTIVE,	NBR_EVT_TIMEOUT,	NBR_ACT_DEL,		NBR_STA_DOWN},
72     {NBR_STA_REQ_RCVD,	NBR_EVT_RESPONSE_SENT,	NBR_ACT_DEL,		NBR_STA_DOWN},
73     {NBR_STA_ACTIVE,	NBR_EVT_RESPONSE_SENT,	NBR_ACT_NOTHING,	NBR_STA_ACTIVE},
74     {NBR_STA_ANY,	NBR_EVT_KILL_NBR,	NBR_ACT_DEL,		NBR_STA_DOWN},
75     {-1,		NBR_EVT_NOTHING,	NBR_ACT_NOTHING,	0},
76 };
77 
78 const char * const nbr_event_names[] = {
79 	"RESPONSE RCVD",
80 	"REQUEST RCVD",
81 	"RESPONSE SENT",
82 	"NBR TIMEOUT",
83 	"NBR KILL",
84 	"NOTHING"
85 };
86 
87 const char * const nbr_action_names[] = {
88 	"START TIMER",
89 	"RESET TIMER",
90 	"DELETE NBR",
91 	"NOTHING"
92 };
93 
94 int
nbr_fsm(struct nbr * nbr,enum nbr_event event)95 nbr_fsm(struct nbr *nbr, enum nbr_event event)
96 {
97 	struct timeval	 now;
98 	int		 old_state;
99 	int		 new_state = 0;
100 	int		 i;
101 
102 	old_state = nbr->state;
103 	for (i = 0; nbr_fsm_tbl[i].state != -1; i++)
104 		if ((nbr_fsm_tbl[i].state & old_state) &&
105 		    (nbr_fsm_tbl[i].event == event)) {
106 			new_state = nbr_fsm_tbl[i].new_state;
107 			break;
108 		}
109 
110 	if (nbr_fsm_tbl[i].state == -1) {
111 		/* event outside of the defined fsm, ignore it. */
112 		log_warnx("nbr_fsm: neighbor ID %s, "
113 		    "event '%s' not expected in state '%s'",
114 		    inet_ntoa(nbr->id), nbr_event_name(event),
115 		    nbr_state_name(old_state));
116 		return (0);
117 	}
118 
119 	switch (nbr_fsm_tbl[i].action) {
120 	case NBR_ACT_RST_TIMER:
121 		nbr_set_timer(nbr);
122 		break;
123 	case NBR_ACT_STRT_TIMER:
124 		nbr_set_timer(nbr);
125 		break;
126 	case NBR_ACT_DEL:
127 		nbr_act_del(nbr);
128 		break;
129 	case NBR_ACT_NOTHING:
130 		/* do nothing */
131 		break;
132 	}
133 
134 	if (new_state != 0)
135 		nbr->state = new_state;
136 
137 	if (old_state != nbr->state) {
138 		/* neighbor changed from/to ACTIVE */
139 		gettimeofday(&now, NULL);
140 		nbr->uptime = now.tv_sec;
141 
142 		log_debug("nbr_fsm: event '%s' resulted in action '%s' and "
143 		    "changing state for neighbor ID %s from '%s' to '%s'",
144 		    nbr_event_name(event),
145 		    nbr_action_name(nbr_fsm_tbl[i].action),
146 		    inet_ntoa(nbr->id), nbr_state_name(old_state),
147 		    nbr_state_name(nbr->state));
148 	}
149 
150 	return (0);
151 }
152 
153 void
nbr_init(u_int32_t hashsize)154 nbr_init(u_int32_t hashsize)
155 {
156 	u_int32_t	 hs, i;
157 
158 	for (hs = 1; hs < hashsize; hs <<= 1)
159 		;
160 	nbrtable.hashtbl = calloc(hs, sizeof(struct nbr_head));
161 	if (nbrtable.hashtbl == NULL)
162 		fatal("nbr_init");
163 
164 	for (i = 0; i < hs; i++)
165 		LIST_INIT(&nbrtable.hashtbl[i]);
166 
167 	nbrtable.hashmask = hs - 1;
168 }
169 
170 struct nbr *
nbr_new(u_int32_t nbr_id,struct iface * iface)171 nbr_new(u_int32_t nbr_id, struct iface *iface)
172 {
173 	struct nbr_head	*head;
174 	struct nbr	*nbr = NULL;
175 
176 	if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
177 		fatal("nbr_new");
178 
179 	nbr->state = NBR_STA_DOWN;
180 	nbr->id.s_addr = nbr_id;
181 
182 	/* get next unused peerid */
183 	while (nbr_find_peerid(++peercnt))
184 		;
185 	nbr->peerid = peercnt;
186 	head = NBR_HASH(nbr->peerid);
187 	LIST_INSERT_HEAD(head, nbr, hash);
188 
189 	/* add to peer list */
190 	nbr->iface = iface;
191 	LIST_INSERT_HEAD(&iface->nbr_list, nbr, entry);
192 
193 	TAILQ_INIT(&nbr->rp_list);
194 	TAILQ_INIT(&nbr->rq_list);
195 
196 	/* set event structures */
197 	evtimer_set(&nbr->timeout_timer, nbr_timeout_timer, nbr);
198 
199 	log_debug("nbr_new: neighbor ID %s, peerid %u",
200 	    inet_ntoa(nbr->id), nbr->peerid);
201 
202 	return (nbr);
203 }
204 
205 void
nbr_del(struct nbr * nbr)206 nbr_del(struct nbr *nbr)
207 {
208 	log_debug("nbr_del: neighbor ID %s, peerid %u", inet_ntoa(nbr->id),
209 	    nbr->peerid);
210 
211 	/* stop timer */
212 	nbr_stop_timer(nbr);
213 
214 	LIST_REMOVE(nbr, entry);
215 	LIST_REMOVE(nbr, hash);
216 
217 	free(nbr);
218 }
219 
220 void
nbr_act_del(struct nbr * nbr)221 nbr_act_del(struct nbr *nbr)
222 {
223 	/* If there is no authentication or it is just a route request
224 	 * there is no need to keep track of the failed neighbors */
225 	if (nbr->iface->auth_type == AUTH_CRYPT &&
226 	    nbr->state != NBR_STA_REQ_RCVD)
227 		nbr_failed_new(nbr);
228 
229 	log_debug("nbr_act_del: neighbor ID %s, peerid %u", inet_ntoa(nbr->id),
230 	    nbr->peerid);
231 
232 	/* schedule kill timer */
233 	nbr_set_timer(nbr);
234 
235 	/* clear lists */
236 	clear_list(&nbr->rq_list);
237 	clear_list(&nbr->rp_list);
238 }
239 
240 struct nbr *
nbr_find_peerid(u_int32_t peerid)241 nbr_find_peerid(u_int32_t peerid)
242 {
243 	struct nbr_head	*head;
244 	struct nbr	*nbr;
245 
246 	head = NBR_HASH(peerid);
247 
248 	LIST_FOREACH(nbr, head, hash) {
249 		if (nbr->peerid == peerid)
250 			return (nbr);
251 	}
252 
253 	return (NULL);
254 }
255 
256 struct nbr *
nbr_find_ip(struct iface * iface,u_int32_t src_ip)257 nbr_find_ip(struct iface *iface, u_int32_t src_ip)
258 {
259 	struct nbr	*nbr = NULL;
260 
261 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
262 		if (nbr->id.s_addr == src_ip) {
263 			return (nbr);
264 		}
265 	}
266 
267 	return (NULL);
268 }
269 
270 /* failed nbr handling */
271 void
nbr_failed_new(struct nbr * nbr)272 nbr_failed_new(struct nbr *nbr)
273 {
274 	struct timeval		 tv;
275 	struct iface		*iface;
276 	struct nbr_failed	*nbr_failed;
277 
278 	if ((nbr_failed = calloc(1, sizeof(*nbr_failed))) == NULL)
279 		fatal("nbr_failed_new");
280 
281 	nbr_failed->addr = nbr->addr;
282 	nbr_failed->auth_seq_num = nbr->auth_seq_num;
283 	iface = nbr->iface;
284 
285 	timerclear(&tv);
286 	tv.tv_sec = FAILED_NBR_TIMEOUT;
287 
288 	evtimer_set(&nbr_failed->timeout_timer, nbr_failed_timeout,
289 	    nbr_failed);
290 
291 	if (evtimer_add(&nbr_failed->timeout_timer, &tv) == -1)
292 		fatal("nbr_failed_new");
293 
294 	LIST_INSERT_HEAD(&iface->failed_nbr_list, nbr_failed, entry);
295 }
296 
297 struct nbr_failed *
nbr_failed_find(struct iface * iface,u_int32_t src_ip)298 nbr_failed_find(struct iface *iface, u_int32_t src_ip)
299 {
300 	struct nbr_failed	*nbr_failed = NULL;
301 
302 	LIST_FOREACH(nbr_failed, &iface->failed_nbr_list, entry) {
303 		if (nbr_failed->addr.s_addr == src_ip) {
304 			return (nbr_failed);
305 		}
306 	}
307 
308 	return (NULL);
309 }
310 
311 void
nbr_failed_delete(struct nbr_failed * nbr_failed)312 nbr_failed_delete(struct nbr_failed *nbr_failed)
313 {
314 	if (evtimer_pending(&nbr_failed->timeout_timer, NULL))
315 		if (evtimer_del(&nbr_failed->timeout_timer) == -1)
316 			fatal("nbr_failed_delete");
317 
318 	LIST_REMOVE(nbr_failed, entry);
319 	free(nbr_failed);
320 }
321 
322 /* timers */
323 void
nbr_timeout_timer(int fd,short event,void * arg)324 nbr_timeout_timer(int fd, short event, void *arg)
325 {
326 	struct nbr *nbr = arg;
327 
328 	if (nbr->state == NBR_STA_DOWN)
329 		nbr_del(nbr);
330 	else
331 		nbr_fsm(nbr, NBR_EVT_TIMEOUT);
332 }
333 
334 void
nbr_failed_timeout(int fd,short event,void * arg)335 nbr_failed_timeout(int fd, short event, void *arg)
336 {
337 	struct nbr_failed	*nbr_failed = arg;
338 
339 	log_debug("nbr_failed_timeout: failed neighbor ID %s deleted",
340 	    inet_ntoa(nbr_failed->addr));
341 
342 	nbr_failed_delete(nbr_failed);
343 }
344 
345 /* actions */
346 void
nbr_set_timer(struct nbr * nbr)347 nbr_set_timer(struct nbr *nbr)
348 {
349 	struct timeval	tv;
350 
351 	timerclear(&tv);
352 	tv.tv_sec = NBR_TIMEOUT;
353 
354 	if (evtimer_add(&nbr->timeout_timer, &tv) == -1)
355 		fatal("nbr_set_timer");
356 }
357 
358 void
nbr_stop_timer(struct nbr * nbr)359 nbr_stop_timer(struct nbr *nbr)
360 {
361 	if (evtimer_del(&nbr->timeout_timer) == -1)
362 		fatal("nbr_stop_timer");
363 }
364 
365 /* names */
366 const char *
nbr_event_name(int event)367 nbr_event_name(int event)
368 {
369 	return (nbr_event_names[event]);
370 }
371 
372 const char *
nbr_action_name(int action)373 nbr_action_name(int action)
374 {
375 	return (nbr_action_names[action]);
376 }
377 
378 struct ctl_nbr *
nbr_to_ctl(struct nbr * nbr)379 nbr_to_ctl(struct nbr *nbr)
380 {
381 	static struct ctl_nbr	 nctl;
382 	struct timeval		 tv, now, res;
383 
384 	memcpy(nctl.name, nbr->iface->name, sizeof(nctl.name));
385 	memcpy(&nctl.id, &nbr->id, sizeof(nctl.id));
386 	memcpy(&nctl.addr, &nbr->addr, sizeof(nctl.addr));
387 
388 	nctl.nbr_state = nbr->state;
389 	nctl.iface_state = nbr->iface->state;
390 
391 	gettimeofday(&now, NULL);
392 	if (evtimer_pending(&nbr->timeout_timer, &tv)) {
393 		timersub(&tv, &now, &res);
394 		if (nbr->state & NBR_STA_DOWN)
395 			nctl.dead_timer = NBR_TIMEOUT - res.tv_sec;
396 		else
397 			nctl.dead_timer = res.tv_sec;
398 	} else
399 		nctl.dead_timer = 0;
400 
401 	if (nbr->state == NBR_STA_ACTIVE) {
402 		nctl.uptime = now.tv_sec - nbr->uptime;
403 	} else
404 		nctl.uptime = 0;
405 
406 	return (&nctl);
407 }
408