1*5b133f3fSguenther /* $OpenBSD: neighbor.c,v 1.51 2023/03/08 04:43:14 guenther 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
53e2993955Sclaudio u_int32_t peercnt = NBR_CNTSTART;
54204df0f8Sclaudio
55204df0f8Sclaudio struct {
56204df0f8Sclaudio int state;
57204df0f8Sclaudio enum nbr_event event;
58204df0f8Sclaudio enum nbr_action action;
59c983ff94Smarkus int new_state; /* 0 means action decides or unchanged */
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_ATTEMPT, NBR_EVT_HELLO_RCVD, NBR_ACT_RST_ITIMER, NBR_STA_INIT},
67204df0f8Sclaudio {NBR_STA_INIT, NBR_EVT_2_WAY_RCVD, NBR_ACT_EVAL, 0},
68c983ff94Smarkus {NBR_STA_XSTRT, NBR_EVT_NEG_DONE, NBR_ACT_SNAP, 0},
69204df0f8Sclaudio {NBR_STA_SNAP, NBR_EVT_SNAP_DONE, NBR_ACT_SNAP_DONE, NBR_STA_XCHNG},
70204df0f8Sclaudio {NBR_STA_XCHNG, NBR_EVT_XCHNG_DONE, NBR_ACT_XCHNG_DONE, 0},
71ddc378ebSclaudio {NBR_STA_LOAD, NBR_EVT_LOAD_DONE, NBR_ACT_NOTHING, NBR_STA_FULL},
72204df0f8Sclaudio {NBR_STA_2_WAY, NBR_EVT_ADJ_OK, NBR_ACT_EVAL, 0},
73204df0f8Sclaudio {NBR_STA_ADJFORM, NBR_EVT_ADJ_OK, NBR_ACT_ADJ_OK, 0},
74204df0f8Sclaudio {NBR_STA_PRELIM, NBR_EVT_ADJ_OK, NBR_ACT_HELLO_CHK, 0},
753922c68bSclaudio {NBR_STA_ADJFORM, NBR_EVT_ADJTMOUT, NBR_ACT_RESTRT_DD, 0},
76c983ff94Smarkus {NBR_STA_FLOOD, NBR_EVT_SEQ_NUM_MIS, NBR_ACT_RESTRT_DD, 0},
77c983ff94Smarkus {NBR_STA_FLOOD, NBR_EVT_BAD_LS_REQ, NBR_ACT_RESTRT_DD, 0},
78204df0f8Sclaudio {NBR_STA_ANY, NBR_EVT_KILL_NBR, NBR_ACT_DEL, NBR_STA_DOWN},
79204df0f8Sclaudio {NBR_STA_ANY, NBR_EVT_LL_DOWN, NBR_ACT_DEL, NBR_STA_DOWN},
80204df0f8Sclaudio {NBR_STA_ANY, NBR_EVT_ITIMER, NBR_ACT_DEL, NBR_STA_DOWN},
81204df0f8Sclaudio {NBR_STA_BIDIR, NBR_EVT_1_WAY_RCVD, NBR_ACT_CLR_LST, NBR_STA_INIT},
82ddc378ebSclaudio {-1, NBR_EVT_NOTHING, NBR_ACT_NOTHING, 0},
83204df0f8Sclaudio };
84204df0f8Sclaudio
85204df0f8Sclaudio const char * const nbr_event_names[] = {
86204df0f8Sclaudio "NOTHING",
87204df0f8Sclaudio "HELLO_RECEIVED",
88204df0f8Sclaudio "2_WAY_RECEIVED",
89204df0f8Sclaudio "NEGOTIATION_DONE",
90204df0f8Sclaudio "SNAPSHOT_DONE",
91204df0f8Sclaudio "EXCHANGE_DONE",
92204df0f8Sclaudio "BAD_LS_REQ",
93204df0f8Sclaudio "LOADING_DONE",
94204df0f8Sclaudio "ADJ_OK",
95204df0f8Sclaudio "SEQ_NUM_MISMATCH",
96204df0f8Sclaudio "1_WAY_RECEIVED",
97204df0f8Sclaudio "KILL_NBR",
98204df0f8Sclaudio "INACTIVITY_TIMER",
99204df0f8Sclaudio "LL_DOWN",
100204df0f8Sclaudio "ADJ_TIMEOUT"
101204df0f8Sclaudio };
102204df0f8Sclaudio
103204df0f8Sclaudio const char * const nbr_action_names[] = {
104204df0f8Sclaudio "NOTHING",
105204df0f8Sclaudio "RESET_INACTIVITY_TIMER",
106204df0f8Sclaudio "START_INACTIVITY_TIMER",
107204df0f8Sclaudio "EVAL",
108204df0f8Sclaudio "SNAPSHOT",
109204df0f8Sclaudio "SNAPSHOT_DONE",
110204df0f8Sclaudio "EXCHANGE_DONE",
111204df0f8Sclaudio "ADJ_OK",
112204df0f8Sclaudio "RESET_DD",
113204df0f8Sclaudio "DELETE",
114a26af33fSclaudio "CLEAR_LISTS",
115a26af33fSclaudio "HELLO_CHK"
116204df0f8Sclaudio };
117204df0f8Sclaudio
118204df0f8Sclaudio int
nbr_fsm(struct nbr * nbr,enum nbr_event event)119204df0f8Sclaudio nbr_fsm(struct nbr *nbr, enum nbr_event event)
120204df0f8Sclaudio {
121129e6b5fSnorby struct timeval now;
122204df0f8Sclaudio int old_state;
123204df0f8Sclaudio int new_state = 0;
124204df0f8Sclaudio int i, ret = 0;
125204df0f8Sclaudio
126204df0f8Sclaudio if (nbr == nbr->iface->self)
127204df0f8Sclaudio return (0);
128204df0f8Sclaudio
129204df0f8Sclaudio old_state = nbr->state;
130204df0f8Sclaudio for (i = 0; nbr_fsm_tbl[i].state != -1; i++)
131204df0f8Sclaudio if ((nbr_fsm_tbl[i].state & old_state) &&
132204df0f8Sclaudio (nbr_fsm_tbl[i].event == event)) {
133204df0f8Sclaudio new_state = nbr_fsm_tbl[i].new_state;
134204df0f8Sclaudio break;
135204df0f8Sclaudio }
136204df0f8Sclaudio
137204df0f8Sclaudio if (nbr_fsm_tbl[i].state == -1) {
138fff6fedaSclaudio /* event outside of the defined fsm, ignore it. */
1399cc19ef1Ssthen log_warnx("nbr_fsm: neighbor ID %s (%s), "
140204df0f8Sclaudio "event %s not expected in state %s",
1419cc19ef1Ssthen inet_ntoa(nbr->id), nbr->iface->name,
1429cc19ef1Ssthen nbr_event_names[event],
143204df0f8Sclaudio nbr_state_name(old_state));
144204df0f8Sclaudio return (0);
145204df0f8Sclaudio }
146204df0f8Sclaudio
147204df0f8Sclaudio switch (nbr_fsm_tbl[i].action) {
148204df0f8Sclaudio case NBR_ACT_RST_ITIMER:
149204df0f8Sclaudio ret = nbr_act_reset_itimer(nbr);
150204df0f8Sclaudio break;
151204df0f8Sclaudio case NBR_ACT_STRT_ITIMER:
152204df0f8Sclaudio ret = nbr_act_start_itimer(nbr);
153204df0f8Sclaudio break;
154204df0f8Sclaudio case NBR_ACT_EVAL:
155204df0f8Sclaudio ret = nbr_act_eval(nbr);
156204df0f8Sclaudio break;
157204df0f8Sclaudio case NBR_ACT_SNAP:
158204df0f8Sclaudio ret = nbr_act_snapshot(nbr);
159204df0f8Sclaudio break;
160204df0f8Sclaudio case NBR_ACT_SNAP_DONE:
161204df0f8Sclaudio /* start db exchange */
162204df0f8Sclaudio start_db_tx_timer(nbr);
163204df0f8Sclaudio break;
164204df0f8Sclaudio case NBR_ACT_XCHNG_DONE:
165204df0f8Sclaudio ret = nbr_act_exchange_done(nbr);
166204df0f8Sclaudio break;
167204df0f8Sclaudio case NBR_ACT_ADJ_OK:
168204df0f8Sclaudio ret = nbr_act_adj_ok(nbr);
169204df0f8Sclaudio break;
170204df0f8Sclaudio case NBR_ACT_RESTRT_DD:
171204df0f8Sclaudio ret = nbr_act_restart_dd(nbr);
172204df0f8Sclaudio break;
173204df0f8Sclaudio case NBR_ACT_DEL:
174204df0f8Sclaudio ret = nbr_act_delete(nbr);
175204df0f8Sclaudio break;
176204df0f8Sclaudio case NBR_ACT_CLR_LST:
177204df0f8Sclaudio ret = nbr_act_clear_lists(nbr);
178204df0f8Sclaudio break;
179204df0f8Sclaudio case NBR_ACT_HELLO_CHK:
180204df0f8Sclaudio ret = nbr_act_hello_check(nbr);
181204df0f8Sclaudio break;
182ddc378ebSclaudio case NBR_ACT_NOTHING:
183204df0f8Sclaudio /* do nothing */
184204df0f8Sclaudio break;
185204df0f8Sclaudio }
186204df0f8Sclaudio
187204df0f8Sclaudio if (ret) {
1889cc19ef1Ssthen log_warnx("nbr_fsm: error changing state for neighbor "
1899cc19ef1Ssthen "ID %s (%s), event %s, state %s",
1909cc19ef1Ssthen inet_ntoa(nbr->id), nbr->iface->name,
191cdd38203Sclaudio nbr_event_names[event], nbr_state_name(old_state));
192204df0f8Sclaudio return (-1);
193204df0f8Sclaudio }
194204df0f8Sclaudio
195204df0f8Sclaudio if (new_state != 0)
196204df0f8Sclaudio nbr->state = new_state;
197204df0f8Sclaudio
1980671d875Sclaudio if (old_state != nbr->state) {
1990671d875Sclaudio nbr->stats.sta_chng++;
20091cc1f69Sclaudio /* state change inform RDE */
201204df0f8Sclaudio ospfe_imsg_compose_rde(IMSG_NEIGHBOR_CHANGE,
20287325ee2Smarkus nbr->peerid, 0, &nbr->state, sizeof(nbr->state));
203204df0f8Sclaudio
2040671d875Sclaudio if (old_state & NBR_STA_FULL || nbr->state & NBR_STA_FULL) {
2050671d875Sclaudio /*
2060671d875Sclaudio * neighbor changed from/to FULL
2070671d875Sclaudio * originate new rtr and net LSA
2080671d875Sclaudio */
20991cc1f69Sclaudio orig_rtr_lsa(nbr->iface->area);
21091cc1f69Sclaudio if (nbr->iface->state & IF_STA_DR)
21191cc1f69Sclaudio orig_net_lsa(nbr->iface);
212129e6b5fSnorby
213129e6b5fSnorby gettimeofday(&now, NULL);
214129e6b5fSnorby nbr->uptime = now.tv_sec;
21591cc1f69Sclaudio }
21691cc1f69Sclaudio
2170671d875Sclaudio /* bidirectional communication lost */
2180671d875Sclaudio if (old_state & ~NBR_STA_PRELIM && nbr->state & NBR_STA_PRELIM)
2190671d875Sclaudio if_fsm(nbr->iface, IF_EVT_NBR_CHNG);
2200671d875Sclaudio
221204df0f8Sclaudio log_debug("nbr_fsm: event %s resulted in action %s and "
2229cc19ef1Ssthen "changing state for neighbor ID %s (%s) from %s to %s",
223cdd38203Sclaudio nbr_event_names[event],
224cdd38203Sclaudio nbr_action_names[nbr_fsm_tbl[i].action],
2259cc19ef1Ssthen inet_ntoa(nbr->id), nbr->iface->name,
2269cc19ef1Ssthen nbr_state_name(old_state),
227204df0f8Sclaudio nbr_state_name(nbr->state));
22811a7e65fSnorby
22911a7e65fSnorby if (nbr->iface->type == IF_TYPE_VIRTUALLINK) {
23011a7e65fSnorby orig_rtr_lsa(nbr->iface->area);
23111a7e65fSnorby }
232204df0f8Sclaudio }
233204df0f8Sclaudio
234204df0f8Sclaudio return (ret);
235204df0f8Sclaudio }
236204df0f8Sclaudio
237204df0f8Sclaudio void
nbr_init(u_int32_t hashsize)238204df0f8Sclaudio nbr_init(u_int32_t hashsize)
239204df0f8Sclaudio {
240e2993955Sclaudio struct nbr_head *head;
241e2993955Sclaudio struct nbr *nbr;
242204df0f8Sclaudio u_int32_t hs, i;
243204df0f8Sclaudio
244204df0f8Sclaudio for (hs = 1; hs < hashsize; hs <<= 1)
245204df0f8Sclaudio ;
246204df0f8Sclaudio nbrtable.hashtbl = calloc(hs, sizeof(struct nbr_head));
247204df0f8Sclaudio if (nbrtable.hashtbl == NULL)
248204df0f8Sclaudio fatal("nbr_init");
249204df0f8Sclaudio
250204df0f8Sclaudio for (i = 0; i < hs; i++)
251204df0f8Sclaudio LIST_INIT(&nbrtable.hashtbl[i]);
252204df0f8Sclaudio
253204df0f8Sclaudio nbrtable.hashmask = hs - 1;
254e2993955Sclaudio
255e2993955Sclaudio /* allocate a dummy neighbor used for self originated AS ext routes */
256e2993955Sclaudio if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
257e2993955Sclaudio fatal("nbr_init");
258e2993955Sclaudio
259e2993955Sclaudio nbr->id.s_addr = ospfe_router_id();
260e2993955Sclaudio nbr->state = NBR_STA_DOWN;
261e2993955Sclaudio nbr->peerid = NBR_IDSELF;
262e2993955Sclaudio head = NBR_HASH(nbr->peerid);
263e2993955Sclaudio LIST_INSERT_HEAD(head, nbr, hash);
264e2993955Sclaudio
265e2993955Sclaudio TAILQ_INIT(&nbr->ls_retrans_list);
266e2993955Sclaudio TAILQ_INIT(&nbr->db_sum_list);
267e2993955Sclaudio TAILQ_INIT(&nbr->ls_req_list);
268204df0f8Sclaudio }
269204df0f8Sclaudio
270204df0f8Sclaudio struct nbr *
nbr_new(u_int32_t nbr_id,struct iface * iface,int self)271204df0f8Sclaudio nbr_new(u_int32_t nbr_id, struct iface *iface, int self)
272204df0f8Sclaudio {
273204df0f8Sclaudio struct nbr_head *head;
274e2993955Sclaudio struct nbr *nbr;
275204df0f8Sclaudio struct rde_nbr rn;
276204df0f8Sclaudio
277204df0f8Sclaudio if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
278204df0f8Sclaudio fatal("nbr_new");
279204df0f8Sclaudio
280204df0f8Sclaudio nbr->state = NBR_STA_DOWN;
2814d267a96Sclaudio nbr->dd_master = 1;
282204df0f8Sclaudio nbr->dd_seq_num = arc4random(); /* RFC: some unique value */
283204df0f8Sclaudio nbr->id.s_addr = nbr_id;
284204df0f8Sclaudio
285204df0f8Sclaudio /* get next unused peerid */
286204df0f8Sclaudio while (nbr_find_peerid(++peercnt))
287204df0f8Sclaudio ;
288204df0f8Sclaudio nbr->peerid = peercnt;
289204df0f8Sclaudio head = NBR_HASH(nbr->peerid);
290204df0f8Sclaudio LIST_INSERT_HEAD(head, nbr, hash);
291204df0f8Sclaudio
292204df0f8Sclaudio /* add to peer list */
293204df0f8Sclaudio nbr->iface = iface;
294204df0f8Sclaudio LIST_INSERT_HEAD(&iface->nbr_list, nbr, entry);
295204df0f8Sclaudio
296204df0f8Sclaudio TAILQ_INIT(&nbr->ls_retrans_list);
297204df0f8Sclaudio TAILQ_INIT(&nbr->db_sum_list);
298204df0f8Sclaudio TAILQ_INIT(&nbr->ls_req_list);
299204df0f8Sclaudio
300204df0f8Sclaudio nbr->ls_req = NULL;
301204df0f8Sclaudio
302204df0f8Sclaudio if (self) {
303204df0f8Sclaudio nbr->state = NBR_STA_FULL;
304204df0f8Sclaudio nbr->addr.s_addr = iface->addr.s_addr;
305204df0f8Sclaudio nbr->priority = iface->priority;
306204df0f8Sclaudio }
307204df0f8Sclaudio
308204df0f8Sclaudio /* set event structures */
309204df0f8Sclaudio evtimer_set(&nbr->inactivity_timer, nbr_itimer, nbr);
310204df0f8Sclaudio evtimer_set(&nbr->db_tx_timer, db_tx_timer, nbr);
311204df0f8Sclaudio evtimer_set(&nbr->lsreq_tx_timer, ls_req_tx_timer, nbr);
312cb75af8bSclaudio evtimer_set(&nbr->ls_retrans_timer, ls_retrans_timer, nbr);
313204df0f8Sclaudio evtimer_set(&nbr->adj_timer, nbr_adj_timer, nbr);
314204df0f8Sclaudio
315204df0f8Sclaudio bzero(&rn, sizeof(rn));
316204df0f8Sclaudio rn.id.s_addr = nbr->id.s_addr;
317204df0f8Sclaudio rn.area_id.s_addr = nbr->iface->area->id.s_addr;
318ef209401Sremi rn.addr.s_addr = nbr->addr.s_addr;
319097ed198Sclaudio rn.ifindex = nbr->iface->ifindex;
320204df0f8Sclaudio rn.state = nbr->state;
321204df0f8Sclaudio rn.self = self;
322204df0f8Sclaudio ospfe_imsg_compose_rde(IMSG_NEIGHBOR_UP, nbr->peerid, 0, &rn,
323204df0f8Sclaudio sizeof(rn));
324204df0f8Sclaudio
325204df0f8Sclaudio return (nbr);
326204df0f8Sclaudio }
327204df0f8Sclaudio
32835277385Sclaudio void
nbr_del(struct nbr * nbr)329204df0f8Sclaudio nbr_del(struct nbr *nbr)
330204df0f8Sclaudio {
331204df0f8Sclaudio ospfe_imsg_compose_rde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, NULL, 0);
332204df0f8Sclaudio
333a302a2ceSclaudio if (evtimer_pending(&nbr->inactivity_timer, NULL))
334a302a2ceSclaudio evtimer_del(&nbr->inactivity_timer);
335a302a2ceSclaudio if (evtimer_pending(&nbr->db_tx_timer, NULL))
336a302a2ceSclaudio evtimer_del(&nbr->db_tx_timer);
337a302a2ceSclaudio if (evtimer_pending(&nbr->lsreq_tx_timer, NULL))
338a302a2ceSclaudio evtimer_del(&nbr->lsreq_tx_timer);
339a302a2ceSclaudio if (evtimer_pending(&nbr->ls_retrans_timer, NULL))
340a302a2ceSclaudio evtimer_del(&nbr->ls_retrans_timer);
341a302a2ceSclaudio if (evtimer_pending(&nbr->adj_timer, NULL))
342a302a2ceSclaudio evtimer_del(&nbr->adj_timer);
343a302a2ceSclaudio
344204df0f8Sclaudio /* clear lists */
345204df0f8Sclaudio ls_retrans_list_clr(nbr);
346204df0f8Sclaudio db_sum_list_clr(nbr);
347204df0f8Sclaudio ls_req_list_clr(nbr);
348204df0f8Sclaudio
34972f25908Sclaudio if (nbr->peerid != NBR_IDSELF)
350204df0f8Sclaudio LIST_REMOVE(nbr, entry);
351204df0f8Sclaudio LIST_REMOVE(nbr, hash);
352204df0f8Sclaudio
353204df0f8Sclaudio free(nbr);
354204df0f8Sclaudio }
355204df0f8Sclaudio
356204df0f8Sclaudio struct nbr *
nbr_find_peerid(u_int32_t peerid)357204df0f8Sclaudio nbr_find_peerid(u_int32_t peerid)
358204df0f8Sclaudio {
359204df0f8Sclaudio struct nbr_head *head;
360204df0f8Sclaudio struct nbr *nbr;
361204df0f8Sclaudio
362204df0f8Sclaudio head = NBR_HASH(peerid);
363204df0f8Sclaudio
364204df0f8Sclaudio LIST_FOREACH(nbr, head, hash) {
365204df0f8Sclaudio if (nbr->peerid == peerid)
366204df0f8Sclaudio return (nbr);
367204df0f8Sclaudio }
368204df0f8Sclaudio
369204df0f8Sclaudio return (NULL);
370204df0f8Sclaudio }
371204df0f8Sclaudio
372204df0f8Sclaudio struct nbr *
nbr_find_id(struct iface * iface,u_int32_t rtr_id)373204df0f8Sclaudio nbr_find_id(struct iface *iface, u_int32_t rtr_id)
374204df0f8Sclaudio {
375204df0f8Sclaudio struct nbr *nbr = NULL;
376204df0f8Sclaudio
377204df0f8Sclaudio LIST_FOREACH(nbr, &iface->nbr_list, entry) {
3783aede346Sclaudio if (nbr->id.s_addr == rtr_id)
379204df0f8Sclaudio return (nbr);
380204df0f8Sclaudio }
381204df0f8Sclaudio
382204df0f8Sclaudio return (NULL);
383204df0f8Sclaudio }
384204df0f8Sclaudio
385204df0f8Sclaudio /* timers */
386204df0f8Sclaudio void
nbr_itimer(int fd,short event,void * arg)387204df0f8Sclaudio nbr_itimer(int fd, short event, void *arg)
388204df0f8Sclaudio {
389204df0f8Sclaudio struct nbr *nbr = arg;
390204df0f8Sclaudio
3913aede346Sclaudio if (nbr->state == NBR_STA_DOWN)
39237cea71bSnorby nbr_del(nbr);
3933aede346Sclaudio else
394204df0f8Sclaudio nbr_fsm(nbr, NBR_EVT_ITIMER);
395204df0f8Sclaudio }
396204df0f8Sclaudio
3973aede346Sclaudio void
nbr_start_itimer(struct nbr * nbr)398204df0f8Sclaudio nbr_start_itimer(struct nbr *nbr)
399204df0f8Sclaudio {
400204df0f8Sclaudio struct timeval tv;
401204df0f8Sclaudio
402204df0f8Sclaudio timerclear(&tv);
403204df0f8Sclaudio tv.tv_sec = nbr->iface->dead_interval;
404204df0f8Sclaudio
4053aede346Sclaudio if (evtimer_add(&nbr->inactivity_timer, &tv) == -1)
4063aede346Sclaudio fatal("nbr_start_itimer");
407204df0f8Sclaudio }
408204df0f8Sclaudio
4093aede346Sclaudio void
nbr_stop_itimer(struct nbr * nbr)410204df0f8Sclaudio nbr_stop_itimer(struct nbr *nbr)
411204df0f8Sclaudio {
4123aede346Sclaudio if (evtimer_del(&nbr->inactivity_timer) == -1)
4133aede346Sclaudio fatal("nbr_stop_itimer");
414204df0f8Sclaudio }
415204df0f8Sclaudio
4163aede346Sclaudio void
nbr_reset_itimer(struct nbr * nbr)417204df0f8Sclaudio nbr_reset_itimer(struct nbr *nbr)
418204df0f8Sclaudio {
419204df0f8Sclaudio struct timeval tv;
420204df0f8Sclaudio
421204df0f8Sclaudio timerclear(&tv);
422204df0f8Sclaudio tv.tv_sec = nbr->iface->dead_interval;
423204df0f8Sclaudio
4243aede346Sclaudio if (evtimer_add(&nbr->inactivity_timer, &tv) == -1)
4253aede346Sclaudio fatal("nbr_reset_itimer");
426204df0f8Sclaudio }
427204df0f8Sclaudio
428204df0f8Sclaudio void
nbr_adj_timer(int fd,short event,void * arg)429204df0f8Sclaudio nbr_adj_timer(int fd, short event, void *arg)
430204df0f8Sclaudio {
431204df0f8Sclaudio struct nbr *nbr = arg;
432204df0f8Sclaudio
43392141c17Smarkus if (!(nbr->state & NBR_STA_ADJFORM))
434718e8ec2Snorby return;
435718e8ec2Snorby
436f71e573aSclaudio if (nbr->state & NBR_STA_ACTIVE && nbr->state != NBR_STA_FULL) {
437699b69fbSsthen log_warnx("nbr_adj_timer: failed to form adjacency with %s "
438699b69fbSsthen "on interface %s", inet_ntoa(nbr->id), nbr->iface->name);
439204df0f8Sclaudio nbr_fsm(nbr, NBR_EVT_ADJTMOUT);
440204df0f8Sclaudio }
441204df0f8Sclaudio }
442204df0f8Sclaudio
4433aede346Sclaudio void
nbr_start_adj_timer(struct nbr * nbr)444204df0f8Sclaudio nbr_start_adj_timer(struct nbr *nbr)
445204df0f8Sclaudio {
446204df0f8Sclaudio struct timeval tv;
447204df0f8Sclaudio
448204df0f8Sclaudio timerclear(&tv);
449204df0f8Sclaudio tv.tv_sec = DEFAULT_ADJ_TMOUT;
450204df0f8Sclaudio
4513aede346Sclaudio if (evtimer_add(&nbr->adj_timer, &tv) == -1)
4523aede346Sclaudio fatal("nbr_start_adj_timer");
453204df0f8Sclaudio }
454204df0f8Sclaudio
455204df0f8Sclaudio /* actions */
456204df0f8Sclaudio int
nbr_act_reset_itimer(struct nbr * nbr)457204df0f8Sclaudio nbr_act_reset_itimer(struct nbr *nbr)
458204df0f8Sclaudio {
4593aede346Sclaudio nbr_reset_itimer(nbr);
460204df0f8Sclaudio
461204df0f8Sclaudio return (0);
462204df0f8Sclaudio }
463204df0f8Sclaudio
464204df0f8Sclaudio int
nbr_act_start_itimer(struct nbr * nbr)465204df0f8Sclaudio nbr_act_start_itimer(struct nbr *nbr)
466204df0f8Sclaudio {
4673aede346Sclaudio nbr_start_itimer(nbr);
468204df0f8Sclaudio
469204df0f8Sclaudio return (0);
470204df0f8Sclaudio }
471204df0f8Sclaudio
472204df0f8Sclaudio int
nbr_adj_ok(struct nbr * nbr)473204df0f8Sclaudio nbr_adj_ok(struct nbr *nbr)
474204df0f8Sclaudio {
475204df0f8Sclaudio struct iface *iface = nbr->iface;
476204df0f8Sclaudio
477204df0f8Sclaudio switch (iface->type) {
478204df0f8Sclaudio case IF_TYPE_POINTOPOINT:
479204df0f8Sclaudio case IF_TYPE_VIRTUALLINK:
480204df0f8Sclaudio case IF_TYPE_POINTOMULTIPOINT:
4813e038149Snorby /* always ok */
482204df0f8Sclaudio break;
483204df0f8Sclaudio case IF_TYPE_BROADCAST:
484204df0f8Sclaudio case IF_TYPE_NBMA:
485204df0f8Sclaudio /*
486204df0f8Sclaudio * if neighbor is dr, bdr or router self is dr or bdr
4879fe0edffSdavid * start forming adjacency
488204df0f8Sclaudio */
489204df0f8Sclaudio if (iface->dr == nbr || iface->bdr == nbr ||
490204df0f8Sclaudio iface->state & IF_STA_DRORBDR)
491204df0f8Sclaudio break;
492204df0f8Sclaudio return (0);
493204df0f8Sclaudio default:
49498c612a2Snorby fatalx("nbr_adj_ok: unknown interface type");
495204df0f8Sclaudio }
496204df0f8Sclaudio return (1);
497204df0f8Sclaudio }
498204df0f8Sclaudio
499204df0f8Sclaudio int
nbr_act_eval(struct nbr * nbr)500204df0f8Sclaudio nbr_act_eval(struct nbr *nbr)
501204df0f8Sclaudio {
502204df0f8Sclaudio if (!nbr_adj_ok(nbr)) {
503204df0f8Sclaudio nbr->state = NBR_STA_2_WAY;
504204df0f8Sclaudio return (0);
505204df0f8Sclaudio }
506204df0f8Sclaudio
507204df0f8Sclaudio nbr->state = NBR_STA_XSTRT;
5084d267a96Sclaudio nbr->dd_master = 1;
509204df0f8Sclaudio nbr->dd_seq_num++; /* as per RFC */
510299d99d9Sclaudio nbr->dd_pending = 0;
511204df0f8Sclaudio /* initial db negotiation */
512204df0f8Sclaudio start_db_tx_timer(nbr);
513204df0f8Sclaudio
5141ccc5a96Snorby nbr_start_adj_timer(nbr);
5151ccc5a96Snorby
516204df0f8Sclaudio return (0);
517204df0f8Sclaudio }
518204df0f8Sclaudio
519204df0f8Sclaudio int
nbr_act_snapshot(struct nbr * nbr)520204df0f8Sclaudio nbr_act_snapshot(struct nbr *nbr)
521204df0f8Sclaudio {
522204df0f8Sclaudio stop_db_tx_timer(nbr);
523204df0f8Sclaudio
524c983ff94Smarkus /* we need to wait for the old snapshot to finish */
525c983ff94Smarkus if (nbr->dd_snapshot) {
526c983ff94Smarkus log_debug("nbr_act_snapshot: giving up, old snapshot running "
5279cc19ef1Ssthen "for neighbor ID %s (%s)", inet_ntoa(nbr->id),
5289cc19ef1Ssthen nbr->iface->name);
529c983ff94Smarkus return (nbr_act_restart_dd(nbr));
530c983ff94Smarkus }
531097ed198Sclaudio ospfe_imsg_compose_rde(IMSG_NEIGHBOR_CAPA, nbr->peerid, 0,
532097ed198Sclaudio &nbr->capa_options, sizeof(nbr->capa_options));
533204df0f8Sclaudio ospfe_imsg_compose_rde(IMSG_DB_SNAPSHOT, nbr->peerid, 0, NULL, 0);
534204df0f8Sclaudio
535c983ff94Smarkus nbr->dd_snapshot = 1; /* wait for IMSG_DB_END */
536c983ff94Smarkus nbr->state = NBR_STA_SNAP;
537c983ff94Smarkus
538204df0f8Sclaudio return (0);
539204df0f8Sclaudio }
540204df0f8Sclaudio
541204df0f8Sclaudio int
nbr_act_exchange_done(struct nbr * nbr)542204df0f8Sclaudio nbr_act_exchange_done(struct nbr *nbr)
543204df0f8Sclaudio {
5444d267a96Sclaudio if (nbr->dd_master)
545204df0f8Sclaudio stop_db_tx_timer(nbr);
546204df0f8Sclaudio
547299d99d9Sclaudio if (ls_req_list_empty(nbr) && nbr->state == NBR_STA_XCHNG &&
548299d99d9Sclaudio nbr->dd_pending == 0) {
549204df0f8Sclaudio nbr->state = NBR_STA_FULL;
550204df0f8Sclaudio return (0);
551204df0f8Sclaudio }
552204df0f8Sclaudio
553204df0f8Sclaudio nbr->state = NBR_STA_LOAD;
554299d99d9Sclaudio
555299d99d9Sclaudio if (!ls_req_list_empty(nbr))
556204df0f8Sclaudio start_ls_req_tx_timer(nbr);
557204df0f8Sclaudio
558204df0f8Sclaudio return (0);
559204df0f8Sclaudio }
560204df0f8Sclaudio
561204df0f8Sclaudio int
nbr_act_adj_ok(struct nbr * nbr)562204df0f8Sclaudio nbr_act_adj_ok(struct nbr *nbr)
563204df0f8Sclaudio {
564204df0f8Sclaudio if (nbr_adj_ok(nbr)) {
565204df0f8Sclaudio if (nbr->state == NBR_STA_2_WAY)
566204df0f8Sclaudio return (nbr_act_eval(nbr));
567204df0f8Sclaudio } else {
568204df0f8Sclaudio nbr->state = NBR_STA_2_WAY;
569204df0f8Sclaudio return (nbr_act_clear_lists(nbr));
570204df0f8Sclaudio }
571204df0f8Sclaudio
572204df0f8Sclaudio return (0);
573204df0f8Sclaudio }
574204df0f8Sclaudio
575204df0f8Sclaudio int
nbr_act_restart_dd(struct nbr * nbr)576204df0f8Sclaudio nbr_act_restart_dd(struct nbr *nbr)
577204df0f8Sclaudio {
5783922c68bSclaudio nbr_act_clear_lists(nbr);
5793922c68bSclaudio
5803922c68bSclaudio if (!nbr_adj_ok(nbr)) {
5813922c68bSclaudio nbr->state = NBR_STA_2_WAY;
5823922c68bSclaudio return (0);
5833922c68bSclaudio }
5843922c68bSclaudio
5853922c68bSclaudio nbr->state = NBR_STA_XSTRT;
5864d267a96Sclaudio nbr->dd_master = 1;
587204df0f8Sclaudio nbr->dd_seq_num += arc4random() & 0xffff;
588299d99d9Sclaudio nbr->dd_pending = 0;
589204df0f8Sclaudio
5903922c68bSclaudio /* initial db negotiation */
5913922c68bSclaudio start_db_tx_timer(nbr);
5923922c68bSclaudio
5931ccc5a96Snorby nbr_start_adj_timer(nbr);
5941ccc5a96Snorby
5953922c68bSclaudio return (0);
596204df0f8Sclaudio }
597204df0f8Sclaudio
598204df0f8Sclaudio int
nbr_act_delete(struct nbr * nbr)599204df0f8Sclaudio nbr_act_delete(struct nbr *nbr)
600204df0f8Sclaudio {
60137cea71bSnorby struct timeval tv;
60237cea71bSnorby
60302ff5b3dSclaudio /* clear dr and bdr */
60447d3fe48Sclaudio nbr->dr.s_addr = 0;
60547d3fe48Sclaudio nbr->bdr.s_addr = 0;
60602ff5b3dSclaudio
60702ff5b3dSclaudio if (nbr == nbr->iface->self)
60835277385Sclaudio return (0);
60935277385Sclaudio
610204df0f8Sclaudio /* stop timers */
6113aede346Sclaudio nbr_stop_itimer(nbr);
612204df0f8Sclaudio
61347d3fe48Sclaudio /* XXX reset crypt_seq_num will allow replay attacks. */
61403431b74Snorby nbr->crypt_seq_num = 0;
61503431b74Snorby
61637cea71bSnorby /* schedule kill timer */
61737cea71bSnorby timerclear(&tv);
61837cea71bSnorby tv.tv_sec = DEFAULT_NBR_TMOUT;
61937cea71bSnorby
62037cea71bSnorby if (evtimer_add(&nbr->inactivity_timer, &tv)) {
6219cc19ef1Ssthen log_warnx("nbr_act_delete: error scheduling "
6229cc19ef1Ssthen "neighbor ID %s (%s) for removal",
6239cc19ef1Ssthen inet_ntoa(nbr->id), nbr->iface->name);
62437cea71bSnorby }
62537cea71bSnorby
626204df0f8Sclaudio return (nbr_act_clear_lists(nbr));
627204df0f8Sclaudio }
628204df0f8Sclaudio
629204df0f8Sclaudio int
nbr_act_clear_lists(struct nbr * nbr)630204df0f8Sclaudio nbr_act_clear_lists(struct nbr *nbr)
631204df0f8Sclaudio {
6323aede346Sclaudio /* stop timers */
6333aede346Sclaudio stop_db_tx_timer(nbr);
6343aede346Sclaudio stop_ls_req_tx_timer(nbr);
635204df0f8Sclaudio
636204df0f8Sclaudio /* clear lists */
637204df0f8Sclaudio ls_retrans_list_clr(nbr);
638204df0f8Sclaudio db_sum_list_clr(nbr);
639204df0f8Sclaudio ls_req_list_clr(nbr);
640204df0f8Sclaudio
641204df0f8Sclaudio return (0);
642204df0f8Sclaudio }
643204df0f8Sclaudio
644204df0f8Sclaudio int
nbr_act_hello_check(struct nbr * nbr)645204df0f8Sclaudio nbr_act_hello_check(struct nbr *nbr)
646204df0f8Sclaudio {
6479cc19ef1Ssthen log_debug("nbr_act_hello_check: neighbor ID %s (%s)",
6489cc19ef1Ssthen inet_ntoa(nbr->id), nbr->iface->name);
649204df0f8Sclaudio
650204df0f8Sclaudio return (-1);
651204df0f8Sclaudio }
652204df0f8Sclaudio
653204df0f8Sclaudio struct ctl_nbr *
nbr_to_ctl(struct nbr * nbr)654204df0f8Sclaudio nbr_to_ctl(struct nbr *nbr)
655204df0f8Sclaudio {
656204df0f8Sclaudio static struct ctl_nbr nctl;
657204df0f8Sclaudio struct timeval tv, now, res;
658204df0f8Sclaudio struct lsa_entry *le;
659204df0f8Sclaudio
660204df0f8Sclaudio memcpy(nctl.name, nbr->iface->name, sizeof(nctl.name));
661204df0f8Sclaudio memcpy(&nctl.id, &nbr->id, sizeof(nctl.id));
662d13813cbSclaudio memcpy(&nctl.addr, &nbr->addr, sizeof(nctl.addr));
663204df0f8Sclaudio memcpy(&nctl.dr, &nbr->dr, sizeof(nctl.dr));
664204df0f8Sclaudio memcpy(&nctl.bdr, &nbr->bdr, sizeof(nctl.bdr));
665204df0f8Sclaudio memcpy(&nctl.area, &nbr->iface->area->id, sizeof(nctl.area));
666204df0f8Sclaudio
667204df0f8Sclaudio /* this list is 99% of the time empty so that's OK for now */
668204df0f8Sclaudio nctl.db_sum_lst_cnt = 0;
669204df0f8Sclaudio TAILQ_FOREACH(le, &nbr->db_sum_list, entry)
670204df0f8Sclaudio nctl.db_sum_lst_cnt++;
671204df0f8Sclaudio
672204df0f8Sclaudio nctl.ls_req_lst_cnt = nbr->ls_req_cnt;
6731ba81fefSnorby nctl.ls_retrans_lst_cnt = nbr->ls_ret_cnt;
674204df0f8Sclaudio
675204df0f8Sclaudio nctl.nbr_state = nbr->state;
676944ca59cSclaudio
677944ca59cSclaudio /*
678944ca59cSclaudio * We need to trick a bit to show the remote iface state.
679944ca59cSclaudio * The idea is to print DR, BDR or DROther dependent on
680944ca59cSclaudio * the type of the neighbor.
681944ca59cSclaudio */
682944ca59cSclaudio if (nbr->iface->dr == nbr)
683944ca59cSclaudio nctl.iface_state = IF_STA_DR;
684944ca59cSclaudio else if (nbr->iface->bdr == nbr)
685944ca59cSclaudio nctl.iface_state = IF_STA_BACKUP;
686944ca59cSclaudio else if (nbr->iface->state & IF_STA_MULTI)
687944ca59cSclaudio nctl.iface_state = IF_STA_DROTHER;
688944ca59cSclaudio else
689204df0f8Sclaudio nctl.iface_state = nbr->iface->state;
690204df0f8Sclaudio
691204df0f8Sclaudio nctl.state_chng_cnt = nbr->stats.sta_chng;
692204df0f8Sclaudio
693204df0f8Sclaudio nctl.priority = nbr->priority;
694097ed198Sclaudio nctl.options = nbr->options | nbr->capa_options;
695204df0f8Sclaudio
696204df0f8Sclaudio gettimeofday(&now, NULL);
697204df0f8Sclaudio if (evtimer_pending(&nbr->inactivity_timer, &tv)) {
698204df0f8Sclaudio timersub(&tv, &now, &res);
699c843cd3cSclaudio if (nbr->state & NBR_STA_DOWN)
700c843cd3cSclaudio nctl.dead_timer = DEFAULT_NBR_TMOUT - res.tv_sec;
701c843cd3cSclaudio else
702204df0f8Sclaudio nctl.dead_timer = res.tv_sec;
703204df0f8Sclaudio } else
704204df0f8Sclaudio nctl.dead_timer = 0;
705204df0f8Sclaudio
706129e6b5fSnorby if (nbr->state == NBR_STA_FULL) {
707129e6b5fSnorby nctl.uptime = now.tv_sec - nbr->uptime;
708129e6b5fSnorby } else
709129e6b5fSnorby nctl.uptime = 0;
710129e6b5fSnorby
711204df0f8Sclaudio return (&nctl);
712204df0f8Sclaudio }
713204df0f8Sclaudio
714204df0f8Sclaudio struct lsa_hdr *
lsa_hdr_new(void)715204df0f8Sclaudio lsa_hdr_new(void)
716204df0f8Sclaudio {
717204df0f8Sclaudio struct lsa_hdr *lsa_hdr = NULL;
718204df0f8Sclaudio
719204df0f8Sclaudio if ((lsa_hdr = calloc(1, sizeof(*lsa_hdr))) == NULL)
720204df0f8Sclaudio fatal("lsa_hdr_new");
721204df0f8Sclaudio
722204df0f8Sclaudio return (lsa_hdr);
723204df0f8Sclaudio }
724