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 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 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 * 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 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 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 * 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 * 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 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 * 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 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 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 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 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 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 * 367 nbr_event_name(int event) 368 { 369 return (nbr_event_names[event]); 370 } 371 372 const char * 373 nbr_action_name(int action) 374 { 375 return (nbr_action_names[action]); 376 } 377 378 struct ctl_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