xref: /openbsd-src/usr.sbin/ldpd/neighbor.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: neighbor.c,v 1.24 2011/03/12 01:57:13 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org>
5  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
6  * Copyright (c) 2004, 2005, 2008 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 #include <unistd.h>
36 
37 #include "ldpd.h"
38 #include "ldp.h"
39 #include "ldpe.h"
40 #include "control.h"
41 #include "log.h"
42 #include "lde.h"
43 
44 int	nbr_establish_connection(struct nbr *);
45 void	nbr_send_labelmappings(struct nbr *);
46 int	nbr_act_session_operational(struct nbr *);
47 
48 static __inline int nbr_id_compare(struct nbr *, struct nbr *);
49 static __inline int nbr_addr_compare(struct nbr *, struct nbr *);
50 static __inline int nbr_pid_compare(struct nbr *, struct nbr *);
51 
52 RB_HEAD(nbr_id_head, nbr);
53 RB_PROTOTYPE(nbr_id_head, nbr, id_tree, nbr_id_compare)
54 RB_GENERATE(nbr_id_head, nbr, id_tree, nbr_id_compare)
55 RB_HEAD(nbr_addr_head, nbr);
56 RB_PROTOTYPE(nbr_addr_head, nbr, addr_tree, nbr_addr_compare)
57 RB_GENERATE(nbr_addr_head, nbr, addr_tree, nbr_addr_compare)
58 RB_HEAD(nbr_pid_head, nbr);
59 RB_PROTOTYPE(nbr_pid_head, nbr, pid_tree, nbr_pid_compare)
60 RB_GENERATE(nbr_pid_head, nbr, pid_tree, nbr_pid_compare)
61 
62 static __inline int
63 nbr_id_compare(struct nbr *a, struct nbr *b)
64 {
65 	if (ntohl(a->id.s_addr) - ntohl(b->id.s_addr) == 0)
66 		return (a->lspace - b->lspace);
67 	return (ntohl(a->id.s_addr) - ntohl(b->id.s_addr));
68 }
69 
70 static __inline int
71 nbr_addr_compare(struct nbr *a, struct nbr *b)
72 {
73 	return (ntohl(a->addr.s_addr) - ntohl(b->addr.s_addr));
74 }
75 
76 static __inline int
77 nbr_pid_compare(struct nbr *a, struct nbr *b)
78 {
79 	return (a->peerid - b->peerid);
80 }
81 
82 struct nbr_id_head nbrs_by_id = RB_INITIALIZER(&nbrs_by_id);
83 struct nbr_addr_head nbrs_by_addr = RB_INITIALIZER(&nbrs_by_addr);
84 struct nbr_pid_head nbrs_by_pid = RB_INITIALIZER(&nbrs_by_pid);
85 
86 u_int32_t	peercnt = NBR_CNTSTART;
87 
88 extern struct ldpd_conf	*leconf;
89 
90 struct {
91 	int		state;
92 	enum nbr_event	event;
93 	enum nbr_action	action;
94 	int		new_state;
95 } nbr_fsm_tbl[] = {
96     /* current state	event that happened	action to take		resulting state */
97 /* Discovery States */
98     {NBR_STA_DOWN,	NBR_EVT_HELLO_RCVD,	NBR_ACT_STRT_ITIMER,	NBR_STA_PRESENT},
99     {NBR_STA_SESSION,	NBR_EVT_HELLO_RCVD,	NBR_ACT_RST_ITIMER,	0},
100 /* Passive Role */
101     {NBR_STA_PRESENT,	NBR_EVT_SESSION_UP,	NBR_ACT_SESSION_EST,	NBR_STA_INITIAL},
102     {NBR_STA_INITIAL,	NBR_EVT_INIT_RCVD,	NBR_ACT_INIT_SEND,	NBR_STA_OPENREC},
103     {NBR_STA_OPENREC,	NBR_EVT_KEEPALIVE_RCVD,	NBR_ACT_STRT_KTIMER,	NBR_STA_OPER},
104 /* Active Role */
105     {NBR_STA_PRESENT,	NBR_EVT_INIT_SENT,	NBR_ACT_NOTHING,	NBR_STA_OPENSENT},
106     {NBR_STA_OPENSENT,	NBR_EVT_INIT_RCVD,	NBR_ACT_KEEPALIVE_SEND,	NBR_STA_OPER},
107 /* Session Maintenance */
108     {NBR_STA_OPER,	NBR_EVT_PDU_RCVD,	NBR_ACT_RST_KTIMEOUT,	0},
109 /* Session Close */
110     {NBR_STA_SESSION,	NBR_EVT_CLOSE_SESSION,	NBR_ACT_CLOSE_SESSION,	NBR_STA_PRESENT},
111     {NBR_STA_SESSION,	NBR_EVT_DOWN,		NBR_ACT_CLOSE_SESSION,	},
112     {-1,		NBR_EVT_NOTHING,	NBR_ACT_NOTHING,	0},
113 };
114 
115 const char * const nbr_event_names[] = {
116 	"NOTHING",
117 	"HELLO RECEIVED",
118 	"SESSION UP",
119 	"SESSION CLOSE",
120 	"INIT RECEIVED",
121 	"KEEPALIVE RECEIVED",
122 	"PDU RECEIVED",
123 	"INIT SENT",
124 	"DOWN"
125 };
126 
127 const char * const nbr_action_names[] = {
128 	"NOTHING",
129 	"START INACTIVITY TIMER",
130 	"RESET INACTIVITY TIMER",
131 	"RESET KEEPALIVE TIMEOUT",
132 	"START KEEPALIVE TIMER",
133 	"RESET KEEPALIVE TIMER",
134 	"START NEIGHBOR SESSION",
135 	"SEND INIT",
136 	"SEND KEEPALIVE",
137 	"CLOSE SESSION"
138 };
139 
140 int
141 nbr_fsm(struct nbr *nbr, enum nbr_event event)
142 {
143 	struct timeval	now;
144 	int		old_state;
145 	int		new_state = 0;
146 	int		i, ret = 0;
147 
148 	old_state = nbr->state;
149 	for (i = 0; nbr_fsm_tbl[i].state != -1; i++)
150 		if ((nbr_fsm_tbl[i].state & old_state) &&
151 		    (nbr_fsm_tbl[i].event == event)) {
152 			new_state = nbr_fsm_tbl[i].new_state;
153 			break;
154 		}
155 
156 	if (nbr_fsm_tbl[i].state == -1) {
157 		/* event outside of the defined fsm, ignore it. */
158 		log_warnx("nbr_fsm: neighbor ID %s, "
159 		    "event %s not expected in state %s",
160 		    inet_ntoa(nbr->id), nbr_event_names[event],
161 		    nbr_state_name(old_state));
162 		return (0);
163 	}
164 
165 	ret = 0;
166 
167 	switch (nbr_fsm_tbl[i].action) {
168 	case NBR_ACT_RST_ITIMER:
169 	case NBR_ACT_STRT_ITIMER:
170 		nbr_start_itimer(nbr);
171 		break;
172 	case NBR_ACT_RST_KTIMEOUT:
173 		nbr_start_ktimeout(nbr);
174 		break;
175 	case NBR_ACT_RST_KTIMER:
176 		nbr_start_ktimer(nbr);
177 		break;
178 	case NBR_ACT_STRT_KTIMER:
179 		nbr_act_session_operational(nbr);
180 		nbr_start_ktimer(nbr);
181 		nbr_start_ktimeout(nbr);
182 		send_address(nbr, NULL);
183 		nbr_send_labelmappings(nbr);
184 		break;
185 	case NBR_ACT_SESSION_EST:
186 		ret = nbr_act_session_establish(nbr, 0);
187 		break;
188 	case NBR_ACT_INIT_SEND:
189 		send_init(nbr);
190 		send_keepalive(nbr);
191 		break;
192 	case NBR_ACT_KEEPALIVE_SEND:
193 		nbr_act_session_operational(nbr);
194 		nbr_start_ktimer(nbr);
195 		nbr_start_ktimeout(nbr);
196 		send_keepalive(nbr);
197 		send_address(nbr, NULL);
198 		nbr_send_labelmappings(nbr);
199 		break;
200 	case NBR_ACT_CLOSE_SESSION:
201 		ldpe_imsg_compose_lde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0,
202 		    NULL, 0);
203 		session_close(nbr);
204 		nbr_start_idtimer(nbr);
205 		break;
206 	case NBR_ACT_NOTHING:
207 		/* do nothing */
208 		break;
209 	}
210 
211 	if (ret) {
212 		log_warnx("nbr_fsm: error changing state for neighbor ID %s, "
213 		    "event %s, state %s", inet_ntoa(nbr->id),
214 		    nbr_event_names[event], nbr_state_name(old_state));
215 		return (-1);
216 	}
217 
218 	if (new_state != 0)
219 		nbr->state = new_state;
220 
221 	if (old_state != nbr->state) {
222 		log_debug("nbr_fsm: event %s resulted in action %s and "
223 		    "changing state for neighbor ID %s from %s to %s",
224 		    nbr_event_names[event],
225 		    nbr_action_names[nbr_fsm_tbl[i].action],
226 		    inet_ntoa(nbr->id), nbr_state_name(old_state),
227 		    nbr_state_name(nbr->state));
228 
229 		if (nbr->state == NBR_STA_OPER) {
230 			gettimeofday(&now, NULL);
231 			nbr->uptime = now.tv_sec;
232 		}
233 	}
234 
235 	return (ret);
236 }
237 
238 struct nbr *
239 nbr_new(u_int32_t nbr_id, u_int16_t lspace, struct iface *iface,
240     struct in_addr a)
241 {
242 	struct nbr	*nbr;
243 
244 	if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
245 		fatal("nbr_new");
246 	if ((nbr->rbuf = calloc(1, sizeof(struct ibuf_read))) == NULL)
247 		fatal("nbr_new");
248 
249 	nbr->state = NBR_STA_DOWN;
250 	nbr->id.s_addr = nbr_id;
251 	nbr->lspace = lspace;
252 	nbr->iface = iface;
253 	nbr->addr = a;
254 
255 	/* get next unused peerid */
256 	while (nbr_find_peerid(++peercnt))
257 		;
258 	nbr->peerid = peercnt;
259 
260 	if (RB_INSERT(nbr_pid_head, &nbrs_by_pid, nbr) != NULL)
261 		fatalx("nbr_new: RB_INSERT(nbrs_by_pid) failed");
262 	if (RB_INSERT(nbr_addr_head, &nbrs_by_addr, nbr) != NULL)
263 		fatalx("nbr_new: RB_INSERT(nbrs_by_addr) failed");
264 	if (RB_INSERT(nbr_id_head, &nbrs_by_id, nbr) != NULL)
265 		fatalx("nbr_new: RB_INSERT(nbrs_by_id) failed");
266 
267 	TAILQ_INIT(&nbr->mapping_list);
268 	TAILQ_INIT(&nbr->withdraw_list);
269 	TAILQ_INIT(&nbr->request_list);
270 	TAILQ_INIT(&nbr->release_list);
271 	TAILQ_INIT(&nbr->abortreq_list);
272 
273 	/* set event structures */
274 	evtimer_set(&nbr->inactivity_timer, nbr_itimer, nbr);
275 	evtimer_set(&nbr->keepalive_timeout, nbr_ktimeout, nbr);
276 	evtimer_set(&nbr->keepalive_timer, nbr_ktimer, nbr);
277 	evtimer_set(&nbr->initdelay_timer, nbr_idtimer, nbr);
278 
279 	return (nbr);
280 }
281 
282 void
283 nbr_del(struct nbr *nbr)
284 {
285 	session_close(nbr);
286 
287 	if (evtimer_pending(&nbr->inactivity_timer, NULL))
288 		evtimer_del(&nbr->inactivity_timer);
289 	if (evtimer_pending(&nbr->keepalive_timer, NULL))
290 		evtimer_del(&nbr->keepalive_timer);
291 	if (evtimer_pending(&nbr->keepalive_timeout, NULL))
292 		evtimer_del(&nbr->keepalive_timeout);
293 	if (evtimer_pending(&nbr->initdelay_timer, NULL))
294 		evtimer_del(&nbr->initdelay_timer);
295 
296 	nbr_mapping_list_clr(nbr, &nbr->mapping_list);
297 
298 	RB_REMOVE(nbr_pid_head, &nbrs_by_pid, nbr);
299 	RB_REMOVE(nbr_addr_head, &nbrs_by_addr, nbr);
300 	RB_REMOVE(nbr_id_head, &nbrs_by_id, nbr);
301 
302 	free(nbr->rbuf);
303 	free(nbr);
304 }
305 
306 struct nbr *
307 nbr_find_peerid(u_int32_t peerid)
308 {
309 	struct nbr	n;
310 	n.peerid = peerid;
311 	return (RB_FIND(nbr_pid_head, &nbrs_by_pid, &n));
312 }
313 
314 struct nbr *
315 nbr_find_ip(u_int32_t addr)
316 {
317 	struct nbr	n;
318 	n.addr.s_addr = addr;
319 	return (RB_FIND(nbr_addr_head, &nbrs_by_addr, &n));
320 }
321 
322 struct nbr *
323 nbr_find_ldpid(u_int32_t rtr_id, u_int16_t lspace)
324 {
325 	struct nbr	n;
326 	n.id.s_addr = rtr_id;
327 	n.lspace = lspace;
328 	return (RB_FIND(nbr_id_head, &nbrs_by_id, &n));
329 }
330 
331 /* timers */
332 
333 /* Inactivity timer: timeout based on hellos */
334 /* ARGSUSED */
335 void
336 nbr_itimer(int fd, short event, void *arg)
337 {
338 	struct nbr *nbr = arg;
339 
340 	log_debug("nbr_itimer: neighbor ID %s peerid %lu", inet_ntoa(nbr->id),
341 	    nbr->peerid);
342 
343 	nbr_del(nbr);
344 }
345 
346 void
347 nbr_start_itimer(struct nbr *nbr)
348 {
349 	struct timeval	tv;
350 
351 	timerclear(&tv);
352 	tv.tv_sec = nbr->holdtime;
353 
354 	if (evtimer_add(&nbr->inactivity_timer, &tv) == -1)
355 		fatal("nbr_start_itimer");
356 }
357 
358 void
359 nbr_stop_itimer(struct nbr *nbr)
360 {
361 	if (evtimer_del(&nbr->inactivity_timer) == -1)
362 		fatal("nbr_stop_itimer");
363 }
364 
365 /* Keepalive timer: timer to send keepalive message to neighbors */
366 
367 void
368 nbr_ktimer(int fd, short event, void *arg)
369 {
370 	struct nbr	*nbr = arg;
371 	struct timeval	 tv;
372 
373 	send_keepalive(nbr);
374 
375 	timerclear(&tv);
376 	tv.tv_sec = (time_t)(nbr->keepalive / KEEPALIVE_PER_PERIOD);
377 	if (evtimer_add(&nbr->keepalive_timer, &tv) == -1)
378 		fatal("nbr_ktimer");
379 }
380 
381 void
382 nbr_start_ktimer(struct nbr *nbr)
383 {
384 	struct timeval	tv;
385 
386 	timerclear(&tv);
387 
388 	/* XXX: just to be sure it will send three keepalives per period */
389 	tv.tv_sec = (time_t)(nbr->keepalive / KEEPALIVE_PER_PERIOD);
390 
391 	if (evtimer_add(&nbr->keepalive_timer, &tv) == -1)
392 		fatal("nbr_start_ktimer");
393 }
394 
395 void
396 nbr_stop_ktimer(struct nbr *nbr)
397 {
398 	if (evtimer_del(&nbr->keepalive_timer) == -1)
399 		fatal("nbr_stop_ktimer");
400 }
401 
402 /* Keepalive timeout: if the nbr hasn't sent keepalive */
403 
404 void
405 nbr_ktimeout(int fd, short event, void *arg)
406 {
407 	struct nbr *nbr = arg;
408 
409 	log_debug("nbr_ktimeout: neighbor ID %s peerid %lu", inet_ntoa(nbr->id),
410 	    nbr->peerid);
411 
412 	send_notification_nbr(nbr, S_KEEPALIVE_TMR, 0, 0);
413 	/* XXX race, send_notification_nbr() has no chance to be sent */
414 	session_close(nbr);
415 }
416 
417 void
418 nbr_start_ktimeout(struct nbr *nbr)
419 {
420 	struct timeval	tv;
421 
422 	timerclear(&tv);
423 	tv.tv_sec = nbr->keepalive;
424 
425 	if (evtimer_add(&nbr->keepalive_timeout, &tv) == -1)
426 		fatal("nbr_start_ktimeout");
427 }
428 
429 void
430 nbr_stop_ktimeout(struct nbr *nbr)
431 {
432 	if (evtimer_del(&nbr->keepalive_timeout) == -1)
433 		fatal("nbr_stop_ktimeout");
434 }
435 
436 /* Init delay timer: timer to retry to iniziatize session */
437 
438 void
439 nbr_idtimer(int fd, short event, void *arg)
440 {
441 	struct nbr *nbr = arg;
442 
443 	if (ntohl(nbr->addr.s_addr) >= ntohl(nbr->iface->addr.s_addr))
444 		return;
445 
446 	log_debug("nbr_idtimer: neighbor ID %s peerid %lu", inet_ntoa(nbr->id),
447 	    nbr->peerid);
448 
449 	nbr_act_session_establish(nbr, 1);
450 }
451 
452 void
453 nbr_start_idtimer(struct nbr *nbr)
454 {
455 	struct timeval	tv;
456 
457 	timerclear(&tv);
458 	tv.tv_sec = INIT_DELAY_TMR;
459 
460 	if (evtimer_add(&nbr->initdelay_timer, &tv) == -1)
461 		fatal("nbr_start_idtimer");
462 }
463 
464 void
465 nbr_stop_idtimer(struct nbr *nbr)
466 {
467 	if (evtimer_del(&nbr->initdelay_timer) == -1)
468 		fatal("nbr_stop_idtimer");
469 }
470 
471 int
472 nbr_pending_idtimer(struct nbr *nbr)
473 {
474 	if (evtimer_pending(&nbr->initdelay_timer, NULL))
475 		return (1);
476 
477 	return (0);
478 }
479 
480 int
481 nbr_establish_connection(struct nbr *nbr)
482 {
483 	struct sockaddr_in	in;
484 
485 	bzero(&in, sizeof(in));
486 	in.sin_family = AF_INET;
487 	in.sin_port = htons(LDP_PORT);
488 	in.sin_addr.s_addr = nbr->addr.s_addr;
489 
490 	nbr->fd = socket(AF_INET, SOCK_STREAM, 0);
491 	if (nbr->fd == -1) {
492 		log_debug("nbr_establish_connection: error while "
493 		    "creating socket");
494 		return (-1);
495 	}
496 
497 	if (connect(nbr->fd, (struct sockaddr *)&in, sizeof(in)) == -1) {
498 		log_debug("nbr_establish_connection: error while "
499 		    "connecting to %s", inet_ntoa(nbr->addr));
500 		nbr_start_idtimer(nbr);
501 		close(nbr->fd);
502 		return (-1);
503 	}
504 
505 	return (0);
506 }
507 
508 int
509 nbr_act_session_establish(struct nbr *nbr, int active)
510 {
511 	if (active) {
512 		if (nbr_establish_connection(nbr) < 0)
513 			return (-1);
514 	}
515 
516 	evbuf_init(&nbr->wbuf, nbr->fd, session_write, nbr);
517 	event_set(&nbr->rev, nbr->fd, EV_READ | EV_PERSIST, session_read, nbr);
518 	event_add(&nbr->rev, NULL);
519 
520 	if (active) {
521 		send_init(nbr);
522 		nbr_fsm(nbr, NBR_EVT_INIT_SENT);
523 	}
524 
525 	return (0);
526 }
527 
528 int
529 nbr_act_session_operational(struct nbr *nbr)
530 {
531 	struct lde_nbr	 rn;
532 
533 	bzero(&rn, sizeof(rn));
534 	rn.id.s_addr = nbr->id.s_addr;
535 	rn.lspace = nbr->lspace;
536 
537 	return (ldpe_imsg_compose_lde(IMSG_NEIGHBOR_UP, nbr->peerid, 0, &rn,
538 	    sizeof(rn)));
539 }
540 
541 void
542 nbr_send_labelmappings(struct nbr *nbr)
543 {
544 	if (leconf->mode & MODE_ADV_UNSOLICITED) {
545 		ldpe_imsg_compose_lde(IMSG_LABEL_MAPPING_FULL, nbr->peerid, 0,
546 		    NULL, 0);
547 	}
548 }
549 
550 void
551 nbr_mapping_add(struct nbr *nbr, struct mapping_head *mh, struct map *map)
552 {
553 	struct mapping_entry	*me;
554 
555 	me = calloc(1, sizeof(*me));
556 	if (me == NULL)
557 		fatal("nbr_mapping_add");
558 	me->map = *map;
559 
560 	TAILQ_INSERT_TAIL(mh, me, entry);
561 }
562 
563 struct mapping_entry *
564 nbr_mapping_find(struct nbr *nbr, struct mapping_head *mh, struct map *map)
565 {
566 	struct mapping_entry	*me = NULL;
567 
568 	TAILQ_FOREACH(me, mh, entry) {
569 		if (me->map.prefix.s_addr == map->prefix.s_addr &&
570 		    me->map.prefixlen == map->prefixlen)
571 			return (me);
572 	}
573 
574 	return (NULL);
575 }
576 
577 void
578 nbr_mapping_del(struct nbr *nbr, struct mapping_head *mh, struct map *map)
579 {
580 	struct mapping_entry	*me;
581 
582 	me = nbr_mapping_find(nbr, mh, map);
583 	if (me == NULL)
584 		return;
585 
586 	TAILQ_REMOVE(mh, me, entry);
587 	free(me);
588 }
589 
590 void
591 nbr_mapping_list_clr(struct nbr *nbr, struct mapping_head *mh)
592 {
593 	struct mapping_entry	*me;
594 
595 	while ((me = TAILQ_FIRST(mh)) != NULL) {
596 		TAILQ_REMOVE(mh, me, entry);
597 		free(me);
598 	}
599 }
600 
601 struct ctl_nbr *
602 nbr_to_ctl(struct nbr *nbr)
603 {
604 	static struct ctl_nbr	 nctl;
605 	struct timeval		 tv, now, res;
606 
607 	memcpy(nctl.name, nbr->iface->name, sizeof(nctl.name));
608 	memcpy(&nctl.id, &nbr->id, sizeof(nctl.id));
609 	memcpy(&nctl.addr, &nbr->addr, sizeof(nctl.addr));
610 
611 	nctl.nbr_state = nbr->state;
612 	nctl.iface_state = nbr->iface->state;
613 
614 	gettimeofday(&now, NULL);
615 	if (evtimer_pending(&nbr->inactivity_timer, &tv)) {
616 		timersub(&tv, &now, &res);
617 		if (nbr->state & NBR_STA_DOWN)
618 			nctl.dead_timer = DEFAULT_NBR_TMOUT - res.tv_sec;
619 		else
620 			nctl.dead_timer = res.tv_sec;
621 	} else
622 		nctl.dead_timer = 0;
623 
624 	if (nbr->state == NBR_STA_OPER) {
625 		nctl.uptime = now.tv_sec - nbr->uptime;
626 	} else
627 		nctl.uptime = 0;
628 
629 	return (&nctl);
630 }
631 
632 void
633 ldpe_nbr_ctl(struct ctl_conn *c)
634 {
635 	struct nbr	*nbr;
636 	struct ctl_nbr	*nctl;
637 
638 	RB_FOREACH(nbr, nbr_addr_head, &nbrs_by_addr) {
639 		nctl = nbr_to_ctl(nbr);
640 		imsg_compose_event(&c->iev, IMSG_CTL_SHOW_NBR, 0, 0, -1, nctl,
641 		    sizeof(struct ctl_nbr));
642 	}
643 	imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0);
644 }
645