xref: /openbsd/usr.sbin/ospfd/neighbor.c (revision cb75af8b)
1*cb75af8bSclaudio /*	$OpenBSD: neighbor.c,v 1.12 2005/03/17 21:17:12 claudio 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/ioctl.h>
22204df0f8Sclaudio #include <sys/time.h>
23204df0f8Sclaudio #include <sys/socket.h>
24204df0f8Sclaudio #include <netinet/in.h>
25204df0f8Sclaudio #include <arpa/inet.h>
26204df0f8Sclaudio #include <net/if.h>
27204df0f8Sclaudio 
28204df0f8Sclaudio #include <ctype.h>
29204df0f8Sclaudio #include <err.h>
30204df0f8Sclaudio #include <stdio.h>
31204df0f8Sclaudio #include <stdlib.h>
32204df0f8Sclaudio #include <string.h>
33204df0f8Sclaudio #include <event.h>
34204df0f8Sclaudio 
35204df0f8Sclaudio #include "ospfd.h"
36204df0f8Sclaudio #include "ospf.h"
37204df0f8Sclaudio #include "ospfe.h"
38204df0f8Sclaudio #include "log.h"
39204df0f8Sclaudio #include "rde.h"
40204df0f8Sclaudio 
41204df0f8Sclaudio int	 nbr_adj_ok(struct nbr *);
42204df0f8Sclaudio 
43204df0f8Sclaudio LIST_HEAD(nbr_head, nbr);
44204df0f8Sclaudio 
45204df0f8Sclaudio struct nbr_table {
46204df0f8Sclaudio 	struct nbr_head		*hashtbl;
47204df0f8Sclaudio 	u_int32_t		 hashmask;
48204df0f8Sclaudio } nbrtable;
49204df0f8Sclaudio 
50204df0f8Sclaudio #define NBR_HASH(x)		\
51204df0f8Sclaudio 	&nbrtable.hashtbl[(x) & nbrtable.hashmask]
52204df0f8Sclaudio 
53204df0f8Sclaudio u_int32_t	peercnt;
54204df0f8Sclaudio 
55204df0f8Sclaudio struct {
56204df0f8Sclaudio 	int		state;
57204df0f8Sclaudio 	enum nbr_event	event;
58204df0f8Sclaudio 	enum nbr_action	action;
59204df0f8Sclaudio 	int		new_state;
60204df0f8Sclaudio } nbr_fsm_tbl[] = {
61204df0f8Sclaudio     /* current state	event that happened	action to take		resulting state */
62204df0f8Sclaudio     {NBR_STA_ACTIVE,	NBR_EVT_HELLO_RCVD,	NBR_ACT_RST_ITIMER,	0},
63ddc378ebSclaudio     {NBR_STA_BIDIR,	NBR_EVT_2_WAY_RCVD,	NBR_ACT_NOTHING,	0},
64ddc378ebSclaudio     {NBR_STA_INIT,	NBR_EVT_1_WAY_RCVD,	NBR_ACT_NOTHING,	0},
65204df0f8Sclaudio     {NBR_STA_DOWN,	NBR_EVT_HELLO_RCVD,	NBR_ACT_STRT_ITIMER,	NBR_STA_INIT},
66204df0f8Sclaudio     {NBR_STA_DOWN,	NBR_EVT_STRT,		NBR_ACT_STRT,		NBR_STA_ATTEMPT},
67204df0f8Sclaudio     {NBR_STA_ATTEMPT,	NBR_EVT_HELLO_RCVD,	NBR_ACT_RST_ITIMER,	NBR_STA_INIT},
68204df0f8Sclaudio     {NBR_STA_INIT,	NBR_EVT_2_WAY_RCVD,	NBR_ACT_EVAL,		0},
69204df0f8Sclaudio     {NBR_STA_XSTRT,	NBR_EVT_NEG_DONE,	NBR_ACT_SNAP,		NBR_STA_SNAP},
70204df0f8Sclaudio     {NBR_STA_SNAP,	NBR_EVT_SNAP_DONE,	NBR_ACT_SNAP_DONE,	NBR_STA_XCHNG},
71204df0f8Sclaudio     {NBR_STA_XCHNG,	NBR_EVT_XCHNG_DONE,	NBR_ACT_XCHNG_DONE,	0},
72ddc378ebSclaudio     {NBR_STA_LOAD,	NBR_EVT_LOAD_DONE,	NBR_ACT_NOTHING,	NBR_STA_FULL},
73204df0f8Sclaudio     {NBR_STA_2_WAY,	NBR_EVT_ADJ_OK,		NBR_ACT_EVAL,		0},
74204df0f8Sclaudio     {NBR_STA_ADJFORM,	NBR_EVT_ADJ_OK,		NBR_ACT_ADJ_OK,		0},
75204df0f8Sclaudio     {NBR_STA_PRELIM,	NBR_EVT_ADJ_OK,		NBR_ACT_HELLO_CHK,	0},
763922c68bSclaudio     {NBR_STA_ADJFORM,	NBR_EVT_ADJTMOUT,	NBR_ACT_RESTRT_DD,	0},
77204df0f8Sclaudio     {NBR_STA_FLOOD,	NBR_EVT_SEQ_NUM_MIS,	NBR_ACT_RESTRT_DD,	NBR_STA_XSTRT},
78204df0f8Sclaudio     {NBR_STA_FLOOD,	NBR_EVT_BAD_LS_REQ,	NBR_ACT_RESTRT_DD,	NBR_STA_XSTRT},
79204df0f8Sclaudio     {NBR_STA_ANY,	NBR_EVT_KILL_NBR,	NBR_ACT_DEL,		NBR_STA_DOWN},
80204df0f8Sclaudio     {NBR_STA_ANY,	NBR_EVT_LL_DOWN,	NBR_ACT_DEL,		NBR_STA_DOWN},
81204df0f8Sclaudio     {NBR_STA_ANY,	NBR_EVT_ITIMER,		NBR_ACT_DEL,		NBR_STA_DOWN},
82204df0f8Sclaudio     {NBR_STA_BIDIR,	NBR_EVT_1_WAY_RCVD,	NBR_ACT_CLR_LST,	NBR_STA_INIT},
83ddc378ebSclaudio     {-1,		NBR_EVT_NOTHING,	NBR_ACT_NOTHING,	0},
84204df0f8Sclaudio };
85204df0f8Sclaudio 
86204df0f8Sclaudio const char * const nbr_event_names[] = {
87204df0f8Sclaudio 	"NOTHING",
88204df0f8Sclaudio 	"HELLO_RECEIVED",
89204df0f8Sclaudio 	"START",
90204df0f8Sclaudio 	"2_WAY_RECEIVED",
91204df0f8Sclaudio 	"NEGOTIATION_DONE",
92204df0f8Sclaudio 	"SNAPSHOT_DONE",
93204df0f8Sclaudio 	"EXCHANGE_DONE",
94204df0f8Sclaudio 	"BAD_LS_REQ",
95204df0f8Sclaudio 	"LOADING_DONE",
96204df0f8Sclaudio 	"ADJ_OK",
97204df0f8Sclaudio 	"SEQ_NUM_MISMATCH",
98204df0f8Sclaudio 	"1_WAY_RECEIVED",
99204df0f8Sclaudio 	"KILL_NBR",
100204df0f8Sclaudio 	"INACTIVITY_TIMER",
101204df0f8Sclaudio 	"LL_DOWN",
102204df0f8Sclaudio 	"ADJ_TIMEOUT"
103204df0f8Sclaudio };
104204df0f8Sclaudio 
105204df0f8Sclaudio const char * const nbr_action_names[] = {
106204df0f8Sclaudio 	"NOTHING",
107204df0f8Sclaudio 	"START",
108204df0f8Sclaudio 	"RESET_INACTIVITY_TIMER",
109204df0f8Sclaudio 	"START_INACTIVITY_TIMER",
110204df0f8Sclaudio 	"EVAL",
111204df0f8Sclaudio 	"SNAPSHOT",
112204df0f8Sclaudio 	"SNAPSHOT_DONE",
113204df0f8Sclaudio 	"EXCHANGE_DONE",
114204df0f8Sclaudio 	"ADJ_OK",
115204df0f8Sclaudio 	"RESET_DD",
116204df0f8Sclaudio 	"DELETE",
117204df0f8Sclaudio 	"CLEAR_LISTS"
118204df0f8Sclaudio };
119204df0f8Sclaudio 
120204df0f8Sclaudio int
121204df0f8Sclaudio nbr_fsm(struct nbr *nbr, enum nbr_event event)
122204df0f8Sclaudio {
123204df0f8Sclaudio 	int		old_state;
124204df0f8Sclaudio 	int		new_state = 0;
125204df0f8Sclaudio 	int		i, ret = 0;
126204df0f8Sclaudio 
127204df0f8Sclaudio 	if (nbr == nbr->iface->self)
128204df0f8Sclaudio 		return (0);
129204df0f8Sclaudio 
130204df0f8Sclaudio 	old_state = nbr->state;
131204df0f8Sclaudio 	for (i = 0; nbr_fsm_tbl[i].state != -1; i++)
132204df0f8Sclaudio 		if ((nbr_fsm_tbl[i].state & old_state) &&
133204df0f8Sclaudio 		    (nbr_fsm_tbl[i].event == event)) {
134204df0f8Sclaudio 			new_state = nbr_fsm_tbl[i].new_state;
135204df0f8Sclaudio 			break;
136204df0f8Sclaudio 		}
137204df0f8Sclaudio 
138204df0f8Sclaudio 	if (nbr_fsm_tbl[i].state == -1) {
139204df0f8Sclaudio 		/* XXX event outside of the defined fsm, ignore it. */
140ddc378ebSclaudio 		log_warnx("nbr_fsm: neighbor ID %s, "
141204df0f8Sclaudio 		    "event %s not expected in state %s",
142204df0f8Sclaudio 		    inet_ntoa(nbr->id), nbr_event_name(event),
143204df0f8Sclaudio 		    nbr_state_name(old_state));
144204df0f8Sclaudio 		return (0);
145204df0f8Sclaudio 	}
146204df0f8Sclaudio 
147204df0f8Sclaudio 	switch (nbr_fsm_tbl[i].action) {
148204df0f8Sclaudio 	case NBR_ACT_STRT:
149204df0f8Sclaudio 		ret = nbr_act_start(nbr);
150204df0f8Sclaudio 		break;
151204df0f8Sclaudio 	case NBR_ACT_RST_ITIMER:
152204df0f8Sclaudio 		ret = nbr_act_reset_itimer(nbr);
153204df0f8Sclaudio 		break;
154204df0f8Sclaudio 	case NBR_ACT_STRT_ITIMER:
155204df0f8Sclaudio 		ret = nbr_act_start_itimer(nbr);
156204df0f8Sclaudio 		break;
157204df0f8Sclaudio 	case NBR_ACT_EVAL:
158204df0f8Sclaudio 		ret = nbr_act_eval(nbr);
159204df0f8Sclaudio 		break;
160204df0f8Sclaudio 	case NBR_ACT_SNAP:
161204df0f8Sclaudio 		ret = nbr_act_snapshot(nbr);
162204df0f8Sclaudio 		break;
163204df0f8Sclaudio 	case NBR_ACT_SNAP_DONE:
164204df0f8Sclaudio 		/* start db exchange */
165204df0f8Sclaudio 		start_db_tx_timer(nbr);
166204df0f8Sclaudio 		break;
167204df0f8Sclaudio 	case NBR_ACT_XCHNG_DONE:
168204df0f8Sclaudio 		ret = nbr_act_exchange_done(nbr);
169204df0f8Sclaudio 		break;
170204df0f8Sclaudio 	case NBR_ACT_ADJ_OK:
171204df0f8Sclaudio 		ret = nbr_act_adj_ok(nbr);
172204df0f8Sclaudio 		break;
173204df0f8Sclaudio 	case NBR_ACT_RESTRT_DD:
174204df0f8Sclaudio 		ret = nbr_act_restart_dd(nbr);
175204df0f8Sclaudio 		break;
176204df0f8Sclaudio 	case NBR_ACT_DEL:
177204df0f8Sclaudio 		ret = nbr_act_delete(nbr);
178204df0f8Sclaudio 		break;
179204df0f8Sclaudio 	case NBR_ACT_CLR_LST:
180204df0f8Sclaudio 		ret = nbr_act_clear_lists(nbr);
181204df0f8Sclaudio 		break;
182204df0f8Sclaudio 	case NBR_ACT_HELLO_CHK:
183204df0f8Sclaudio 		ret = nbr_act_hello_check(nbr);
184204df0f8Sclaudio 		break;
185ddc378ebSclaudio 	case NBR_ACT_NOTHING:
186204df0f8Sclaudio 		/* do nothing */
187204df0f8Sclaudio 		break;
188204df0f8Sclaudio 	}
189204df0f8Sclaudio 
190204df0f8Sclaudio 	if (ret) {
191ddc378ebSclaudio 		log_warnx("nbr_fsm: error changing state for neighbor ID %s, "
192204df0f8Sclaudio 		    "event %s, state %s", inet_ntoa(nbr->id),
193204df0f8Sclaudio 		    nbr_event_name(event), nbr_state_name(old_state));
194204df0f8Sclaudio 		return (-1);
195204df0f8Sclaudio 	}
196204df0f8Sclaudio 
197204df0f8Sclaudio 	if (new_state != 0)
198204df0f8Sclaudio 		nbr->state = new_state;
199204df0f8Sclaudio 
20091cc1f69Sclaudio 	/* state change inform RDE */
20191cc1f69Sclaudio 	if (old_state != nbr->state)
202204df0f8Sclaudio 		ospfe_imsg_compose_rde(IMSG_NEIGHBOR_CHANGE,
203204df0f8Sclaudio 		    nbr->peerid, 0, &new_state, sizeof(new_state));
204204df0f8Sclaudio 
205204df0f8Sclaudio 	/* bidirectional communication lost */
20691cc1f69Sclaudio 	if (old_state & ~NBR_STA_PRELIM && nbr->state & NBR_STA_PRELIM)
207204df0f8Sclaudio 		if_fsm(nbr->iface, IF_EVT_NBR_CHNG);
208204df0f8Sclaudio 
20991cc1f69Sclaudio 	/* neighbor changed from/to FULL originate new rtr and net LSA */
21091cc1f69Sclaudio 	if (old_state != nbr->state && (old_state & NBR_STA_FULL ||
21191cc1f69Sclaudio 	    nbr->state & NBR_STA_FULL)) {
21291cc1f69Sclaudio 		orig_rtr_lsa(nbr->iface->area);
21391cc1f69Sclaudio 		if (nbr->iface->state & IF_STA_DR)
21491cc1f69Sclaudio 			orig_net_lsa(nbr->iface);
21591cc1f69Sclaudio 	}
21691cc1f69Sclaudio 
217204df0f8Sclaudio 	if (old_state != nbr->state) {
218204df0f8Sclaudio 		nbr->stats.sta_chng++;
219204df0f8Sclaudio 		log_debug("nbr_fsm: event %s resulted in action %s and "
220204df0f8Sclaudio 		    "changing state for neighbor ID %s from %s to %s",
221204df0f8Sclaudio 		    nbr_event_name(event),
222204df0f8Sclaudio 		    nbr_action_name(nbr_fsm_tbl[i].action),
223204df0f8Sclaudio 		    inet_ntoa(nbr->id), nbr_state_name(old_state),
224204df0f8Sclaudio 		    nbr_state_name(nbr->state));
225204df0f8Sclaudio 	}
226204df0f8Sclaudio 
227204df0f8Sclaudio 	return (ret);
228204df0f8Sclaudio }
229204df0f8Sclaudio 
230204df0f8Sclaudio void
231204df0f8Sclaudio nbr_init(u_int32_t hashsize)
232204df0f8Sclaudio {
233204df0f8Sclaudio 	u_int32_t        hs, i;
234204df0f8Sclaudio 
235204df0f8Sclaudio 	for (hs = 1; hs < hashsize; hs <<= 1)
236204df0f8Sclaudio 		;
237204df0f8Sclaudio 	nbrtable.hashtbl = calloc(hs, sizeof(struct nbr_head));
238204df0f8Sclaudio 	if (nbrtable.hashtbl == NULL)
239204df0f8Sclaudio 		fatal("nbr_init");
240204df0f8Sclaudio 
241204df0f8Sclaudio 	for (i = 0; i < hs; i++)
242204df0f8Sclaudio 		LIST_INIT(&nbrtable.hashtbl[i]);
243204df0f8Sclaudio 
244204df0f8Sclaudio 	nbrtable.hashmask = hs - 1;
245204df0f8Sclaudio }
246204df0f8Sclaudio 
247204df0f8Sclaudio struct nbr *
248204df0f8Sclaudio nbr_new(u_int32_t nbr_id, struct iface *iface, int self)
249204df0f8Sclaudio {
250204df0f8Sclaudio 	struct nbr_head	*head;
251204df0f8Sclaudio 	struct nbr	*nbr = NULL;
252204df0f8Sclaudio 	struct rde_nbr	 rn;
253204df0f8Sclaudio 
254204df0f8Sclaudio 	if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
255204df0f8Sclaudio 		fatal("nbr_new");
256204df0f8Sclaudio 
257204df0f8Sclaudio 	nbr->state = NBR_STA_DOWN;
258204df0f8Sclaudio 	nbr->master = true;
259204df0f8Sclaudio 	nbr->dd_seq_num = arc4random();	/* RFC: some unique value */
260204df0f8Sclaudio 	nbr->id.s_addr = nbr_id;
261204df0f8Sclaudio 
262204df0f8Sclaudio 	/* get next unused peerid */
263204df0f8Sclaudio 	while (nbr_find_peerid(++peercnt))
264204df0f8Sclaudio 		;
265204df0f8Sclaudio 	nbr->peerid = peercnt;
266204df0f8Sclaudio 	head = NBR_HASH(nbr->peerid);
267204df0f8Sclaudio 	LIST_INSERT_HEAD(head, nbr, hash);
268204df0f8Sclaudio 
269204df0f8Sclaudio 	/* add to peer list */
270204df0f8Sclaudio 	nbr->iface = iface;
271204df0f8Sclaudio 	LIST_INSERT_HEAD(&iface->nbr_list, nbr, entry);
272204df0f8Sclaudio 
273204df0f8Sclaudio 	TAILQ_INIT(&nbr->ls_retrans_list);
274204df0f8Sclaudio 	TAILQ_INIT(&nbr->db_sum_list);
275204df0f8Sclaudio 	TAILQ_INIT(&nbr->ls_req_list);
276204df0f8Sclaudio 
277204df0f8Sclaudio 	nbr->ls_req = NULL;
278204df0f8Sclaudio 
279204df0f8Sclaudio 	if (self) {
280204df0f8Sclaudio 		nbr->state = NBR_STA_FULL;
281204df0f8Sclaudio 		nbr->addr.s_addr = iface->addr.s_addr;
282204df0f8Sclaudio 		nbr->priority = iface->priority;
283204df0f8Sclaudio 	}
284204df0f8Sclaudio 
285204df0f8Sclaudio 	/* set event structures */
286204df0f8Sclaudio 	evtimer_set(&nbr->inactivity_timer, nbr_itimer, nbr);
287204df0f8Sclaudio 	evtimer_set(&nbr->db_tx_timer, db_tx_timer, nbr);
288204df0f8Sclaudio 	evtimer_set(&nbr->lsreq_tx_timer, ls_req_tx_timer, nbr);
289*cb75af8bSclaudio 	evtimer_set(&nbr->ls_retrans_timer, ls_retrans_timer, nbr);
290204df0f8Sclaudio 	evtimer_set(&nbr->adj_timer, nbr_adj_timer, nbr);
291204df0f8Sclaudio 
292204df0f8Sclaudio 	log_debug("nbr_new: neighbor ID %s, peerid %lu",
293204df0f8Sclaudio 	    inet_ntoa(nbr->id), nbr->peerid);
294204df0f8Sclaudio 
295204df0f8Sclaudio 	bzero(&rn, sizeof(rn));
296204df0f8Sclaudio 	rn.id.s_addr = nbr->id.s_addr;
297204df0f8Sclaudio 	rn.area_id.s_addr = nbr->iface->area->id.s_addr;
298204df0f8Sclaudio 	rn.state = nbr->state;
299204df0f8Sclaudio 	rn.self = self;
300204df0f8Sclaudio 	ospfe_imsg_compose_rde(IMSG_NEIGHBOR_UP, nbr->peerid, 0, &rn,
301204df0f8Sclaudio 	    sizeof(rn));
302204df0f8Sclaudio 
303204df0f8Sclaudio 	return (nbr);
304204df0f8Sclaudio }
305204df0f8Sclaudio 
306204df0f8Sclaudio int
307204df0f8Sclaudio nbr_del(struct nbr *nbr)
308204df0f8Sclaudio {
309204df0f8Sclaudio 	log_debug("nbr_del: neighbor ID %s, peerid %lu", inet_ntoa(nbr->id),
310204df0f8Sclaudio 	    nbr->peerid);
311204df0f8Sclaudio 
312204df0f8Sclaudio 	if (nbr == nbr->iface->self)
313204df0f8Sclaudio 		return (0);
314204df0f8Sclaudio 
315204df0f8Sclaudio 	ospfe_imsg_compose_rde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, NULL, 0);
316204df0f8Sclaudio 
317204df0f8Sclaudio 	/* clear lists */
318204df0f8Sclaudio 	ls_retrans_list_clr(nbr);
319204df0f8Sclaudio 	db_sum_list_clr(nbr);
320204df0f8Sclaudio 	ls_req_list_clr(nbr);
321204df0f8Sclaudio 
322204df0f8Sclaudio 	LIST_REMOVE(nbr, entry);
323204df0f8Sclaudio 	LIST_REMOVE(nbr, hash);
324204df0f8Sclaudio 
325204df0f8Sclaudio 	free(nbr);
326204df0f8Sclaudio 
327204df0f8Sclaudio 	return (0);
328204df0f8Sclaudio }
329204df0f8Sclaudio 
330204df0f8Sclaudio struct nbr *
331204df0f8Sclaudio nbr_find_peerid(u_int32_t peerid)
332204df0f8Sclaudio {
333204df0f8Sclaudio 	struct nbr_head	*head;
334204df0f8Sclaudio 	struct nbr	*nbr;
335204df0f8Sclaudio 
336204df0f8Sclaudio 	head = NBR_HASH(peerid);
337204df0f8Sclaudio 
338204df0f8Sclaudio 	LIST_FOREACH(nbr, head, hash) {
339204df0f8Sclaudio 		if (nbr->peerid == peerid)
340204df0f8Sclaudio 			return (nbr);
341204df0f8Sclaudio 	}
342204df0f8Sclaudio 
343204df0f8Sclaudio 	return (NULL);
344204df0f8Sclaudio }
345204df0f8Sclaudio 
346204df0f8Sclaudio struct nbr *
347204df0f8Sclaudio nbr_find_id(struct iface *iface, u_int32_t rtr_id)
348204df0f8Sclaudio {
349204df0f8Sclaudio 	struct nbr	*nbr = NULL;
350204df0f8Sclaudio 
351204df0f8Sclaudio 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
352204df0f8Sclaudio 		if (nbr->id.s_addr == rtr_id) {
353204df0f8Sclaudio 			return (nbr);
354204df0f8Sclaudio 		}
355204df0f8Sclaudio 	}
356204df0f8Sclaudio 
357204df0f8Sclaudio 	return (NULL);
358204df0f8Sclaudio }
359204df0f8Sclaudio 
360204df0f8Sclaudio /* timers */
361204df0f8Sclaudio void
362204df0f8Sclaudio nbr_itimer(int fd, short event, void *arg)
363204df0f8Sclaudio {
364204df0f8Sclaudio 	struct nbr *nbr = arg;
365204df0f8Sclaudio 
366204df0f8Sclaudio 	log_debug("nbr_itimer: %s", inet_ntoa(nbr->id));
367204df0f8Sclaudio 
36837cea71bSnorby 	if (nbr->state == NBR_STA_DOWN) {
36937cea71bSnorby 		nbr_del(nbr);
37037cea71bSnorby 	} else
371204df0f8Sclaudio 		nbr_fsm(nbr, NBR_EVT_ITIMER);
372204df0f8Sclaudio }
373204df0f8Sclaudio 
374204df0f8Sclaudio int
375204df0f8Sclaudio nbr_start_itimer(struct nbr *nbr)
376204df0f8Sclaudio {
377204df0f8Sclaudio 	struct timeval	tv;
378204df0f8Sclaudio 
379204df0f8Sclaudio 	log_debug("nbr_start_itimer: %s", inet_ntoa(nbr->id));
380204df0f8Sclaudio 
381204df0f8Sclaudio 	timerclear(&tv);
382204df0f8Sclaudio 	tv.tv_sec = nbr->iface->dead_interval;
383204df0f8Sclaudio 
384204df0f8Sclaudio 	return (evtimer_add(&nbr->inactivity_timer, &tv));
385204df0f8Sclaudio }
386204df0f8Sclaudio 
387204df0f8Sclaudio int
388204df0f8Sclaudio nbr_stop_itimer(struct nbr *nbr)
389204df0f8Sclaudio {
390204df0f8Sclaudio 	return (evtimer_del(&nbr->inactivity_timer));
391204df0f8Sclaudio }
392204df0f8Sclaudio 
393204df0f8Sclaudio int
394204df0f8Sclaudio nbr_reset_itimer(struct nbr *nbr)
395204df0f8Sclaudio {
396204df0f8Sclaudio 	struct timeval	tv;
397204df0f8Sclaudio 
398204df0f8Sclaudio 	timerclear(&tv);
399204df0f8Sclaudio 	tv.tv_sec = nbr->iface->dead_interval;
400204df0f8Sclaudio 
401204df0f8Sclaudio 	return (evtimer_add(&nbr->inactivity_timer, &tv));
402204df0f8Sclaudio }
403204df0f8Sclaudio 
404204df0f8Sclaudio void
405204df0f8Sclaudio nbr_adj_timer(int fd, short event, void *arg)
406204df0f8Sclaudio {
407204df0f8Sclaudio 	struct nbr *nbr = arg;
408204df0f8Sclaudio 
409f71e573aSclaudio 	if (nbr->state & NBR_STA_ACTIVE && nbr->state != NBR_STA_FULL) {
410204df0f8Sclaudio 		log_debug("nbr_adj_timer: failed to form adjacency");
411204df0f8Sclaudio 		nbr_fsm(nbr, NBR_EVT_ADJTMOUT);
412204df0f8Sclaudio 	}
413204df0f8Sclaudio }
414204df0f8Sclaudio 
415204df0f8Sclaudio int
416204df0f8Sclaudio nbr_start_adj_timer(struct nbr *nbr)
417204df0f8Sclaudio {
418204df0f8Sclaudio 	struct timeval	tv;
419204df0f8Sclaudio 
420204df0f8Sclaudio 	log_debug("nbr_start_adj_timer: %s", inet_ntoa(nbr->id));
421204df0f8Sclaudio 
422204df0f8Sclaudio 	timerclear(&tv);
423204df0f8Sclaudio 	tv.tv_sec = DEFAULT_ADJ_TMOUT;
424204df0f8Sclaudio 
425204df0f8Sclaudio 	return (evtimer_add(&nbr->adj_timer, &tv));
426204df0f8Sclaudio }
427204df0f8Sclaudio 
428204df0f8Sclaudio /* actions */
429204df0f8Sclaudio int
430204df0f8Sclaudio nbr_act_start(struct nbr *nbr)
431204df0f8Sclaudio {
432204df0f8Sclaudio 	log_debug("nbr_act_start: neighbor ID %s", inet_ntoa(nbr->id));
433204df0f8Sclaudio 
434204df0f8Sclaudio 	return (-1);
435204df0f8Sclaudio }
436204df0f8Sclaudio 
437204df0f8Sclaudio int
438204df0f8Sclaudio nbr_act_reset_itimer(struct nbr *nbr)
439204df0f8Sclaudio {
440204df0f8Sclaudio 	if (nbr_reset_itimer(nbr)) {
441204df0f8Sclaudio 		log_warnx("nbr_act_reset_itimer: cannot schedule inactivity "
442204df0f8Sclaudio 		    "timer, neighbor ID %s", inet_ntoa(nbr->id));
443204df0f8Sclaudio 		return (-1);
444204df0f8Sclaudio 	}
445204df0f8Sclaudio 
446204df0f8Sclaudio 	return (0);
447204df0f8Sclaudio }
448204df0f8Sclaudio 
449204df0f8Sclaudio int
450204df0f8Sclaudio nbr_act_start_itimer(struct nbr *nbr)
451204df0f8Sclaudio {
452204df0f8Sclaudio 	if (nbr_start_itimer(nbr)) {
453204df0f8Sclaudio 		log_warnx("nbr_act_start_itimer: cannot schedule inactivity "
454204df0f8Sclaudio 		    "timer, neighbor ID %s",
455204df0f8Sclaudio 		    inet_ntoa(nbr->id));
456204df0f8Sclaudio 		return (-1);
457204df0f8Sclaudio 	}
458204df0f8Sclaudio 
459204df0f8Sclaudio 	return (0);
460204df0f8Sclaudio }
461204df0f8Sclaudio 
462204df0f8Sclaudio int
463204df0f8Sclaudio nbr_adj_ok(struct nbr *nbr)
464204df0f8Sclaudio {
465204df0f8Sclaudio 	struct iface	*iface = nbr->iface;
466204df0f8Sclaudio 
467204df0f8Sclaudio 	switch (iface->type) {
468204df0f8Sclaudio 	case IF_TYPE_POINTOPOINT:
469204df0f8Sclaudio 	case IF_TYPE_VIRTUALLINK:
470204df0f8Sclaudio 	case IF_TYPE_POINTOMULTIPOINT:
471204df0f8Sclaudio 		break;
472204df0f8Sclaudio 	case IF_TYPE_BROADCAST:
473204df0f8Sclaudio 	case IF_TYPE_NBMA:
474204df0f8Sclaudio 		/*
475204df0f8Sclaudio 		 * if neighbor is dr, bdr or router self is dr or bdr
476204df0f8Sclaudio 		 * start forming adjacancy
477204df0f8Sclaudio 		 */
478204df0f8Sclaudio 		if (iface->dr == nbr || iface->bdr == nbr ||
479204df0f8Sclaudio 		    iface->state & IF_STA_DRORBDR)
480204df0f8Sclaudio 			break;
481204df0f8Sclaudio 		return (0);
482204df0f8Sclaudio 	default:
483204df0f8Sclaudio 		fatalx("nbr_act_ok: unknown interface type");
484204df0f8Sclaudio 	}
485204df0f8Sclaudio 	return (1);
486204df0f8Sclaudio }
487204df0f8Sclaudio 
488204df0f8Sclaudio int
489204df0f8Sclaudio nbr_act_eval(struct nbr *nbr)
490204df0f8Sclaudio {
491204df0f8Sclaudio 	log_debug("nbr_act_eval: neighbor ID %s", inet_ntoa(nbr->id));
492204df0f8Sclaudio 
493204df0f8Sclaudio 	if (!nbr_adj_ok(nbr)) {
494204df0f8Sclaudio 		nbr->state = NBR_STA_2_WAY;
495204df0f8Sclaudio 		return (0);
496204df0f8Sclaudio 	}
497204df0f8Sclaudio 
498204df0f8Sclaudio 	nbr->state = NBR_STA_XSTRT;
499204df0f8Sclaudio 	nbr->master = true;
500204df0f8Sclaudio 	nbr->dd_seq_num++;	/* as per RFC */
501299d99d9Sclaudio 	nbr->dd_pending = 0;
502204df0f8Sclaudio 	/* initial db negotiation */
503204df0f8Sclaudio 	start_db_tx_timer(nbr);
504204df0f8Sclaudio 
505204df0f8Sclaudio 	return (0);
506204df0f8Sclaudio }
507204df0f8Sclaudio 
508204df0f8Sclaudio int
509204df0f8Sclaudio nbr_act_snapshot(struct nbr *nbr)
510204df0f8Sclaudio {
511204df0f8Sclaudio 	log_debug("nbr_act_snapshot: neighbor ID %s", inet_ntoa(nbr->id));
512204df0f8Sclaudio 
513204df0f8Sclaudio 	stop_db_tx_timer(nbr);
514204df0f8Sclaudio 	nbr_start_adj_timer(nbr);
515204df0f8Sclaudio 
516204df0f8Sclaudio 	ospfe_imsg_compose_rde(IMSG_DB_SNAPSHOT, nbr->peerid, 0, NULL, 0);
517204df0f8Sclaudio 
518204df0f8Sclaudio 	return (0);
519204df0f8Sclaudio }
520204df0f8Sclaudio 
521204df0f8Sclaudio int
522204df0f8Sclaudio nbr_act_exchange_done(struct nbr *nbr)
523204df0f8Sclaudio {
524204df0f8Sclaudio 	log_debug("nbr_act_exchange_done: neighbor ID %s", inet_ntoa(nbr->id));
525204df0f8Sclaudio 
526204df0f8Sclaudio 	if (nbr->master)
527204df0f8Sclaudio 		stop_db_tx_timer(nbr);
528204df0f8Sclaudio 
529299d99d9Sclaudio 	if (ls_req_list_empty(nbr) && nbr->state == NBR_STA_XCHNG &&
530299d99d9Sclaudio 	    nbr->dd_pending == 0) {
531204df0f8Sclaudio 		nbr->state = NBR_STA_FULL;
532204df0f8Sclaudio 		return (0);
533204df0f8Sclaudio 	}
534204df0f8Sclaudio 
535204df0f8Sclaudio 	nbr->state = NBR_STA_LOAD;
536299d99d9Sclaudio 
537299d99d9Sclaudio 	if (!ls_req_list_empty(nbr))
538204df0f8Sclaudio 		start_ls_req_tx_timer(nbr);
539204df0f8Sclaudio 
540204df0f8Sclaudio 	return (0);
541204df0f8Sclaudio }
542204df0f8Sclaudio 
543204df0f8Sclaudio int
544204df0f8Sclaudio nbr_act_adj_ok(struct nbr *nbr)
545204df0f8Sclaudio {
546204df0f8Sclaudio 	log_debug("nbr_act_adj_ok: neighbor ID %s", inet_ntoa(nbr->id));
547204df0f8Sclaudio 
548204df0f8Sclaudio 	if (nbr_adj_ok(nbr)) {
549204df0f8Sclaudio 		if (nbr->state == NBR_STA_2_WAY)
550204df0f8Sclaudio 			return (nbr_act_eval(nbr));
551204df0f8Sclaudio 	} else {
552204df0f8Sclaudio 		nbr->state = NBR_STA_2_WAY;
553204df0f8Sclaudio 		return (nbr_act_clear_lists(nbr));
554204df0f8Sclaudio 	}
555204df0f8Sclaudio 
556204df0f8Sclaudio 	return (0);
557204df0f8Sclaudio }
558204df0f8Sclaudio 
559204df0f8Sclaudio int
560204df0f8Sclaudio nbr_act_restart_dd(struct nbr *nbr)
561204df0f8Sclaudio {
562204df0f8Sclaudio 	log_debug("nbr_act_restart_dd: neighbor ID %s", inet_ntoa(nbr->id));
563204df0f8Sclaudio 
5643922c68bSclaudio 	nbr_act_clear_lists(nbr);
5653922c68bSclaudio 
5663922c68bSclaudio 	if (!nbr_adj_ok(nbr)) {
5673922c68bSclaudio 		nbr->state = NBR_STA_2_WAY;
5683922c68bSclaudio 		return (0);
5693922c68bSclaudio 	}
5703922c68bSclaudio 
5713922c68bSclaudio 	nbr->state = NBR_STA_XSTRT;
572204df0f8Sclaudio 	nbr->master = true;
573204df0f8Sclaudio 	nbr->dd_seq_num += arc4random() & 0xffff;
574299d99d9Sclaudio 	nbr->dd_pending = 0;
575204df0f8Sclaudio 
5763922c68bSclaudio 	/* initial db negotiation */
5773922c68bSclaudio 	start_db_tx_timer(nbr);
5783922c68bSclaudio 
5793922c68bSclaudio 	return (0);
580204df0f8Sclaudio }
581204df0f8Sclaudio 
582204df0f8Sclaudio int
583204df0f8Sclaudio nbr_act_delete(struct nbr *nbr)
584204df0f8Sclaudio {
58537cea71bSnorby 	struct timeval	tv;
58637cea71bSnorby 
587204df0f8Sclaudio 	log_debug("nbr_act_delete: neighbor ID %s", inet_ntoa(nbr->id));
588204df0f8Sclaudio 
589204df0f8Sclaudio 	/* stop timers */
590204df0f8Sclaudio 	if (nbr_stop_itimer(nbr)) {
591204df0f8Sclaudio 		log_warnx("nbr_act_delete: error removing inactivity timer, "
592204df0f8Sclaudio 		    "neighbor ID %s", inet_ntoa(nbr->id));
593204df0f8Sclaudio 		return (-1);
594204df0f8Sclaudio 	}
595204df0f8Sclaudio 
596204df0f8Sclaudio 	/* clear dr and bdr */
597204df0f8Sclaudio 	nbr->dr.s_addr = 0;
598204df0f8Sclaudio 	nbr->bdr.s_addr = 0;
599204df0f8Sclaudio 
60037cea71bSnorby 	/* schedule kill timer */
60137cea71bSnorby 	timerclear(&tv);
60237cea71bSnorby 	tv.tv_sec = DEFAULT_NBR_TMOUT;
60337cea71bSnorby 
60437cea71bSnorby 	if (evtimer_add(&nbr->inactivity_timer, &tv)) {
60537cea71bSnorby 		log_warnx("nbr_act_delete: error scheduling neighbor ID %s "
60637cea71bSnorby 		    "for removal", inet_ntoa(nbr->id));
60737cea71bSnorby 	}
60837cea71bSnorby 
609204df0f8Sclaudio 	return (nbr_act_clear_lists(nbr));
610204df0f8Sclaudio }
611204df0f8Sclaudio 
612204df0f8Sclaudio int
613204df0f8Sclaudio nbr_act_clear_lists(struct nbr *nbr)
614204df0f8Sclaudio {
615204df0f8Sclaudio 	log_debug("nbr_act_clear_lists: neighbor ID %s", inet_ntoa(nbr->id));
616204df0f8Sclaudio 
617204df0f8Sclaudio 	if (stop_db_tx_timer(nbr)) {
618204df0f8Sclaudio 		log_warnx("nbr_act_delete: error removing db_tx_timer, "
619204df0f8Sclaudio 		    "neighbor ID %s", inet_ntoa(nbr->id));
620204df0f8Sclaudio 		return (-1);
621204df0f8Sclaudio 	}
622204df0f8Sclaudio 
623204df0f8Sclaudio 	if (stop_ls_req_tx_timer(nbr)) {
624204df0f8Sclaudio 		log_warnx("nbr_act_delete: error removing lsreq_tx_timer, "
625204df0f8Sclaudio 		    "neighbor ID %s", inet_ntoa(nbr->id));
626204df0f8Sclaudio 		return (-1);
627204df0f8Sclaudio 	}
628204df0f8Sclaudio 
629204df0f8Sclaudio 	/* clear lists */
630204df0f8Sclaudio 	ls_retrans_list_clr(nbr);
631204df0f8Sclaudio 	db_sum_list_clr(nbr);
632204df0f8Sclaudio 	ls_req_list_clr(nbr);
633204df0f8Sclaudio 
634204df0f8Sclaudio 	return (0);
635204df0f8Sclaudio }
636204df0f8Sclaudio 
637204df0f8Sclaudio int
638204df0f8Sclaudio nbr_act_hello_check(struct nbr *nbr)
639204df0f8Sclaudio {
640204df0f8Sclaudio 	log_debug("nbr_act_hello_check: neighbor ID %s", inet_ntoa(nbr->id));
641204df0f8Sclaudio 
642204df0f8Sclaudio 	return (-1);
643204df0f8Sclaudio }
644204df0f8Sclaudio 
645204df0f8Sclaudio struct ctl_nbr *
646204df0f8Sclaudio nbr_to_ctl(struct nbr *nbr)
647204df0f8Sclaudio {
648204df0f8Sclaudio 	static struct ctl_nbr	 nctl;
649204df0f8Sclaudio 	struct timeval		 tv, now, res;
650204df0f8Sclaudio 	struct lsa_entry	*le;
651204df0f8Sclaudio 
652204df0f8Sclaudio 	memcpy(nctl.name, nbr->iface->name, sizeof(nctl.name));
653204df0f8Sclaudio 	memcpy(&nctl.id, &nbr->id, sizeof(nctl.id));
654d13813cbSclaudio 	memcpy(&nctl.addr, &nbr->addr, sizeof(nctl.addr));
655204df0f8Sclaudio 	memcpy(&nctl.dr, &nbr->dr, sizeof(nctl.dr));
656204df0f8Sclaudio 	memcpy(&nctl.bdr, &nbr->bdr, sizeof(nctl.bdr));
657204df0f8Sclaudio 	memcpy(&nctl.area, &nbr->iface->area->id, sizeof(nctl.area));
658204df0f8Sclaudio 
659204df0f8Sclaudio 	/* this list is 99% of the time empty so that's OK for now */
660204df0f8Sclaudio 	nctl.db_sum_lst_cnt = 0;
661204df0f8Sclaudio 	TAILQ_FOREACH(le, &nbr->db_sum_list, entry)
662204df0f8Sclaudio 		nctl.db_sum_lst_cnt++;
663204df0f8Sclaudio 
664204df0f8Sclaudio 	nctl.ls_req_lst_cnt = nbr->ls_req_cnt;
665204df0f8Sclaudio 
666204df0f8Sclaudio 	/* XXX */
667204df0f8Sclaudio 	nctl.ls_retrans_lst_cnt = 0;
668204df0f8Sclaudio 	TAILQ_FOREACH(le, &nbr->ls_retrans_list, entry)
669204df0f8Sclaudio 		nctl.ls_retrans_lst_cnt++;
670204df0f8Sclaudio 
671204df0f8Sclaudio 	nctl.nbr_state = nbr->state;
672944ca59cSclaudio 
673944ca59cSclaudio 	/*
674944ca59cSclaudio 	 * We need to trick a bit to show the remote iface state.
675944ca59cSclaudio 	 * The idea is to print DR, BDR or DROther dependent on
676944ca59cSclaudio 	 * the type of the neighbor.
677944ca59cSclaudio 	 */
678944ca59cSclaudio 	if (nbr->iface->dr == nbr)
679944ca59cSclaudio 		nctl.iface_state = IF_STA_DR;
680944ca59cSclaudio 	else if (nbr->iface->bdr == nbr)
681944ca59cSclaudio 		nctl.iface_state = IF_STA_BACKUP;
682944ca59cSclaudio 	else if (nbr->iface->state & IF_STA_MULTI)
683944ca59cSclaudio 		nctl.iface_state = IF_STA_DROTHER;
684944ca59cSclaudio 	else
685204df0f8Sclaudio 		nctl.iface_state = nbr->iface->state;
686204df0f8Sclaudio 
687204df0f8Sclaudio 	nctl.state_chng_cnt = nbr->stats.sta_chng;
688204df0f8Sclaudio 
689204df0f8Sclaudio 	nctl.priority = nbr->priority;
690204df0f8Sclaudio 	nctl.options = nbr->options;
691204df0f8Sclaudio 
692204df0f8Sclaudio 	gettimeofday(&now, NULL);
693204df0f8Sclaudio 	if (evtimer_pending(&nbr->inactivity_timer, &tv)) {
694204df0f8Sclaudio 		timersub(&tv, &now, &res);
695204df0f8Sclaudio 		nctl.dead_timer = res.tv_sec;
696204df0f8Sclaudio 	} else
697204df0f8Sclaudio 		nctl.dead_timer = 0;
698204df0f8Sclaudio 
699204df0f8Sclaudio 	return (&nctl);
700204df0f8Sclaudio }
701204df0f8Sclaudio 
702204df0f8Sclaudio /* names */
703204df0f8Sclaudio const char *
704204df0f8Sclaudio nbr_state_name(int state)
705204df0f8Sclaudio {
706204df0f8Sclaudio 	switch (state) {
707204df0f8Sclaudio 	case NBR_STA_DOWN:
708204df0f8Sclaudio 		return ("DOWN");
709204df0f8Sclaudio 	case NBR_STA_ATTEMPT:
710204df0f8Sclaudio 		return ("ATTEMPT");
711204df0f8Sclaudio 	case NBR_STA_INIT:
712204df0f8Sclaudio 		return ("INIT");
713204df0f8Sclaudio 	case NBR_STA_2_WAY:
714204df0f8Sclaudio 		return ("2-WAY");
715204df0f8Sclaudio 	case NBR_STA_XSTRT:
716204df0f8Sclaudio 		return ("EXSTART");
717204df0f8Sclaudio 	case NBR_STA_SNAP:
718204df0f8Sclaudio 		return ("SNAPSHOT");
719204df0f8Sclaudio 	case NBR_STA_XCHNG:
720204df0f8Sclaudio 		return ("EXCHANGE");
721204df0f8Sclaudio 	case NBR_STA_LOAD:
722204df0f8Sclaudio 		return ("LOADING");
723204df0f8Sclaudio 	case NBR_STA_FULL:
724204df0f8Sclaudio 		return ("FULL");
725204df0f8Sclaudio 	default:
726204df0f8Sclaudio 		return ("UNKNOWN");
727204df0f8Sclaudio 	}
728204df0f8Sclaudio }
729204df0f8Sclaudio 
730204df0f8Sclaudio const char *
731204df0f8Sclaudio nbr_event_name(int event)
732204df0f8Sclaudio {
733204df0f8Sclaudio 	return (nbr_event_names[event]);
734204df0f8Sclaudio }
735204df0f8Sclaudio 
736204df0f8Sclaudio const char *
737204df0f8Sclaudio nbr_action_name(int action)
738204df0f8Sclaudio {
739204df0f8Sclaudio 	return (nbr_action_names[action]);
740204df0f8Sclaudio }
741204df0f8Sclaudio 
742204df0f8Sclaudio struct lsa_hdr *
743204df0f8Sclaudio lsa_hdr_new(void)
744204df0f8Sclaudio {
745204df0f8Sclaudio 	struct lsa_hdr	*lsa_hdr = NULL;
746204df0f8Sclaudio 
747204df0f8Sclaudio 	if ((lsa_hdr = calloc(1, sizeof(*lsa_hdr))) == NULL)
748204df0f8Sclaudio 		fatal("lsa_hdr_new");
749204df0f8Sclaudio 
750204df0f8Sclaudio 	return (lsa_hdr);
751204df0f8Sclaudio }
752