1 /* $OpenBSD: neighbor.c,v 1.12 2023/03/08 04:43:14 guenther Exp $ */
2
3 /*
4 * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
5 * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
6 * Copyright (c) 2004, 2005, 2006 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
36 #include "ripd.h"
37 #include "rip.h"
38 #include "ripe.h"
39 #include "log.h"
40 #include "rde.h"
41
42 void nbr_set_timer(struct nbr *);
43 void nbr_stop_timer(struct nbr *);
44
45 void nbr_failed_new(struct nbr *);
46 void nbr_failed_timeout(int, short, void *);
47
48 LIST_HEAD(nbr_head, nbr);
49
50 struct nbr_table {
51 struct nbr_head *hashtbl;
52 u_int32_t hashmask;
53 } nbrtable;
54
55 #define NBR_HASH(x) \
56 &nbrtable.hashtbl[(x) & nbrtable.hashmask]
57
58 u_int32_t peercnt = NBR_CNTSTART;
59
60 struct {
61 int state;
62 enum nbr_event event;
63 enum nbr_action action;
64 int new_state;
65 } nbr_fsm_tbl[] = {
66 /* current state event that happened action to take resulting state */
67 {NBR_STA_DOWN, NBR_EVT_REQUEST_RCVD, NBR_ACT_NOTHING, NBR_STA_REQ_RCVD},
68 {NBR_STA_DOWN, NBR_EVT_RESPONSE_RCVD, NBR_ACT_STRT_TIMER, NBR_STA_ACTIVE},
69 {NBR_STA_ACTIVE, NBR_EVT_RESPONSE_RCVD, NBR_ACT_RST_TIMER, NBR_STA_ACTIVE},
70 {NBR_STA_ACTIVE, NBR_EVT_REQUEST_RCVD, NBR_ACT_NOTHING, NBR_STA_ACTIVE},
71 {NBR_STA_ACTIVE, NBR_EVT_TIMEOUT, NBR_ACT_DEL, NBR_STA_DOWN},
72 {NBR_STA_REQ_RCVD, NBR_EVT_RESPONSE_SENT, NBR_ACT_DEL, NBR_STA_DOWN},
73 {NBR_STA_ACTIVE, NBR_EVT_RESPONSE_SENT, NBR_ACT_NOTHING, NBR_STA_ACTIVE},
74 {NBR_STA_ANY, NBR_EVT_KILL_NBR, NBR_ACT_DEL, NBR_STA_DOWN},
75 {-1, NBR_EVT_NOTHING, NBR_ACT_NOTHING, 0},
76 };
77
78 const char * const nbr_event_names[] = {
79 "RESPONSE RCVD",
80 "REQUEST RCVD",
81 "RESPONSE SENT",
82 "NBR TIMEOUT",
83 "NBR KILL",
84 "NOTHING"
85 };
86
87 const char * const nbr_action_names[] = {
88 "START TIMER",
89 "RESET TIMER",
90 "DELETE NBR",
91 "NOTHING"
92 };
93
94 int
nbr_fsm(struct nbr * nbr,enum nbr_event event)95 nbr_fsm(struct nbr *nbr, enum nbr_event event)
96 {
97 struct timeval now;
98 int old_state;
99 int new_state = 0;
100 int i;
101
102 old_state = nbr->state;
103 for (i = 0; nbr_fsm_tbl[i].state != -1; i++)
104 if ((nbr_fsm_tbl[i].state & old_state) &&
105 (nbr_fsm_tbl[i].event == event)) {
106 new_state = nbr_fsm_tbl[i].new_state;
107 break;
108 }
109
110 if (nbr_fsm_tbl[i].state == -1) {
111 /* event outside of the defined fsm, ignore it. */
112 log_warnx("nbr_fsm: neighbor ID %s, "
113 "event '%s' not expected in state '%s'",
114 inet_ntoa(nbr->id), nbr_event_name(event),
115 nbr_state_name(old_state));
116 return (0);
117 }
118
119 switch (nbr_fsm_tbl[i].action) {
120 case NBR_ACT_RST_TIMER:
121 nbr_set_timer(nbr);
122 break;
123 case NBR_ACT_STRT_TIMER:
124 nbr_set_timer(nbr);
125 break;
126 case NBR_ACT_DEL:
127 nbr_act_del(nbr);
128 break;
129 case NBR_ACT_NOTHING:
130 /* do nothing */
131 break;
132 }
133
134 if (new_state != 0)
135 nbr->state = new_state;
136
137 if (old_state != nbr->state) {
138 /* neighbor changed from/to ACTIVE */
139 gettimeofday(&now, NULL);
140 nbr->uptime = now.tv_sec;
141
142 log_debug("nbr_fsm: event '%s' resulted in action '%s' and "
143 "changing state for neighbor ID %s from '%s' to '%s'",
144 nbr_event_name(event),
145 nbr_action_name(nbr_fsm_tbl[i].action),
146 inet_ntoa(nbr->id), nbr_state_name(old_state),
147 nbr_state_name(nbr->state));
148 }
149
150 return (0);
151 }
152
153 void
nbr_init(u_int32_t hashsize)154 nbr_init(u_int32_t hashsize)
155 {
156 u_int32_t hs, i;
157
158 for (hs = 1; hs < hashsize; hs <<= 1)
159 ;
160 nbrtable.hashtbl = calloc(hs, sizeof(struct nbr_head));
161 if (nbrtable.hashtbl == NULL)
162 fatal("nbr_init");
163
164 for (i = 0; i < hs; i++)
165 LIST_INIT(&nbrtable.hashtbl[i]);
166
167 nbrtable.hashmask = hs - 1;
168 }
169
170 struct nbr *
nbr_new(u_int32_t nbr_id,struct iface * iface)171 nbr_new(u_int32_t nbr_id, struct iface *iface)
172 {
173 struct nbr_head *head;
174 struct nbr *nbr = NULL;
175
176 if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
177 fatal("nbr_new");
178
179 nbr->state = NBR_STA_DOWN;
180 nbr->id.s_addr = nbr_id;
181
182 /* get next unused peerid */
183 while (nbr_find_peerid(++peercnt))
184 ;
185 nbr->peerid = peercnt;
186 head = NBR_HASH(nbr->peerid);
187 LIST_INSERT_HEAD(head, nbr, hash);
188
189 /* add to peer list */
190 nbr->iface = iface;
191 LIST_INSERT_HEAD(&iface->nbr_list, nbr, entry);
192
193 TAILQ_INIT(&nbr->rp_list);
194 TAILQ_INIT(&nbr->rq_list);
195
196 /* set event structures */
197 evtimer_set(&nbr->timeout_timer, nbr_timeout_timer, nbr);
198
199 log_debug("nbr_new: neighbor ID %s, peerid %u",
200 inet_ntoa(nbr->id), nbr->peerid);
201
202 return (nbr);
203 }
204
205 void
nbr_del(struct nbr * nbr)206 nbr_del(struct nbr *nbr)
207 {
208 log_debug("nbr_del: neighbor ID %s, peerid %u", inet_ntoa(nbr->id),
209 nbr->peerid);
210
211 /* stop timer */
212 nbr_stop_timer(nbr);
213
214 LIST_REMOVE(nbr, entry);
215 LIST_REMOVE(nbr, hash);
216
217 free(nbr);
218 }
219
220 void
nbr_act_del(struct nbr * nbr)221 nbr_act_del(struct nbr *nbr)
222 {
223 /* If there is no authentication or it is just a route request
224 * there is no need to keep track of the failed neighbors */
225 if (nbr->iface->auth_type == AUTH_CRYPT &&
226 nbr->state != NBR_STA_REQ_RCVD)
227 nbr_failed_new(nbr);
228
229 log_debug("nbr_act_del: neighbor ID %s, peerid %u", inet_ntoa(nbr->id),
230 nbr->peerid);
231
232 /* schedule kill timer */
233 nbr_set_timer(nbr);
234
235 /* clear lists */
236 clear_list(&nbr->rq_list);
237 clear_list(&nbr->rp_list);
238 }
239
240 struct nbr *
nbr_find_peerid(u_int32_t peerid)241 nbr_find_peerid(u_int32_t peerid)
242 {
243 struct nbr_head *head;
244 struct nbr *nbr;
245
246 head = NBR_HASH(peerid);
247
248 LIST_FOREACH(nbr, head, hash) {
249 if (nbr->peerid == peerid)
250 return (nbr);
251 }
252
253 return (NULL);
254 }
255
256 struct nbr *
nbr_find_ip(struct iface * iface,u_int32_t src_ip)257 nbr_find_ip(struct iface *iface, u_int32_t src_ip)
258 {
259 struct nbr *nbr = NULL;
260
261 LIST_FOREACH(nbr, &iface->nbr_list, entry) {
262 if (nbr->id.s_addr == src_ip) {
263 return (nbr);
264 }
265 }
266
267 return (NULL);
268 }
269
270 /* failed nbr handling */
271 void
nbr_failed_new(struct nbr * nbr)272 nbr_failed_new(struct nbr *nbr)
273 {
274 struct timeval tv;
275 struct iface *iface;
276 struct nbr_failed *nbr_failed;
277
278 if ((nbr_failed = calloc(1, sizeof(*nbr_failed))) == NULL)
279 fatal("nbr_failed_new");
280
281 nbr_failed->addr = nbr->addr;
282 nbr_failed->auth_seq_num = nbr->auth_seq_num;
283 iface = nbr->iface;
284
285 timerclear(&tv);
286 tv.tv_sec = FAILED_NBR_TIMEOUT;
287
288 evtimer_set(&nbr_failed->timeout_timer, nbr_failed_timeout,
289 nbr_failed);
290
291 if (evtimer_add(&nbr_failed->timeout_timer, &tv) == -1)
292 fatal("nbr_failed_new");
293
294 LIST_INSERT_HEAD(&iface->failed_nbr_list, nbr_failed, entry);
295 }
296
297 struct nbr_failed *
nbr_failed_find(struct iface * iface,u_int32_t src_ip)298 nbr_failed_find(struct iface *iface, u_int32_t src_ip)
299 {
300 struct nbr_failed *nbr_failed = NULL;
301
302 LIST_FOREACH(nbr_failed, &iface->failed_nbr_list, entry) {
303 if (nbr_failed->addr.s_addr == src_ip) {
304 return (nbr_failed);
305 }
306 }
307
308 return (NULL);
309 }
310
311 void
nbr_failed_delete(struct nbr_failed * nbr_failed)312 nbr_failed_delete(struct nbr_failed *nbr_failed)
313 {
314 if (evtimer_pending(&nbr_failed->timeout_timer, NULL))
315 if (evtimer_del(&nbr_failed->timeout_timer) == -1)
316 fatal("nbr_failed_delete");
317
318 LIST_REMOVE(nbr_failed, entry);
319 free(nbr_failed);
320 }
321
322 /* timers */
323 void
nbr_timeout_timer(int fd,short event,void * arg)324 nbr_timeout_timer(int fd, short event, void *arg)
325 {
326 struct nbr *nbr = arg;
327
328 if (nbr->state == NBR_STA_DOWN)
329 nbr_del(nbr);
330 else
331 nbr_fsm(nbr, NBR_EVT_TIMEOUT);
332 }
333
334 void
nbr_failed_timeout(int fd,short event,void * arg)335 nbr_failed_timeout(int fd, short event, void *arg)
336 {
337 struct nbr_failed *nbr_failed = arg;
338
339 log_debug("nbr_failed_timeout: failed neighbor ID %s deleted",
340 inet_ntoa(nbr_failed->addr));
341
342 nbr_failed_delete(nbr_failed);
343 }
344
345 /* actions */
346 void
nbr_set_timer(struct nbr * nbr)347 nbr_set_timer(struct nbr *nbr)
348 {
349 struct timeval tv;
350
351 timerclear(&tv);
352 tv.tv_sec = NBR_TIMEOUT;
353
354 if (evtimer_add(&nbr->timeout_timer, &tv) == -1)
355 fatal("nbr_set_timer");
356 }
357
358 void
nbr_stop_timer(struct nbr * nbr)359 nbr_stop_timer(struct nbr *nbr)
360 {
361 if (evtimer_del(&nbr->timeout_timer) == -1)
362 fatal("nbr_stop_timer");
363 }
364
365 /* names */
366 const char *
nbr_event_name(int event)367 nbr_event_name(int event)
368 {
369 return (nbr_event_names[event]);
370 }
371
372 const char *
nbr_action_name(int action)373 nbr_action_name(int action)
374 {
375 return (nbr_action_names[action]);
376 }
377
378 struct ctl_nbr *
nbr_to_ctl(struct nbr * nbr)379 nbr_to_ctl(struct nbr *nbr)
380 {
381 static struct ctl_nbr nctl;
382 struct timeval tv, now, res;
383
384 memcpy(nctl.name, nbr->iface->name, sizeof(nctl.name));
385 memcpy(&nctl.id, &nbr->id, sizeof(nctl.id));
386 memcpy(&nctl.addr, &nbr->addr, sizeof(nctl.addr));
387
388 nctl.nbr_state = nbr->state;
389 nctl.iface_state = nbr->iface->state;
390
391 gettimeofday(&now, NULL);
392 if (evtimer_pending(&nbr->timeout_timer, &tv)) {
393 timersub(&tv, &now, &res);
394 if (nbr->state & NBR_STA_DOWN)
395 nctl.dead_timer = NBR_TIMEOUT - res.tv_sec;
396 else
397 nctl.dead_timer = res.tv_sec;
398 } else
399 nctl.dead_timer = 0;
400
401 if (nbr->state == NBR_STA_ACTIVE) {
402 nctl.uptime = now.tv_sec - nbr->uptime;
403 } else
404 nctl.uptime = 0;
405
406 return (&nctl);
407 }
408