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
nbr_fsm(struct nbr * nbr,enum nbr_event event)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
nbr_init(u_int32_t hashsize)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 *
nbr_new(u_int32_t nbr_id,struct iface * iface,int self)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
nbr_del(struct nbr * nbr)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 *
nbr_find_peerid(u_int32_t peerid)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 *
nbr_find_ip(struct iface * iface,u_int32_t src_ip)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
nbr_itimer(int fd,short event,void * arg)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
nbr_start_itimer(struct nbr * nbr)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
nbr_stop_itimer(struct nbr * nbr)289 nbr_stop_itimer(struct nbr *nbr)
290 {
291 return (evtimer_del(&nbr->inactivity_timer));
292 }
293
294 int
nbr_reset_itimer(struct nbr * nbr)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
nbr_act_start(struct nbr * nbr)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
nbr_act_reset_itimer(struct nbr * nbr)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
nbr_act_start_itimer(struct nbr * nbr)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
nbr_act_delete(struct nbr * nbr)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
nbr_act_clear_lists(struct nbr * nbr)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 *
nbr_event_name(int event)381 nbr_event_name(int event)
382 {
383 return (nbr_event_names[event]);
384 }
385
386 const char *
nbr_action_name(int action)387 nbr_action_name(int action)
388 {
389 return (nbr_action_names[action]);
390 }
391
392 struct ctl_nbr *
nbr_to_ctl(struct nbr * 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