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