1 /* $OpenBSD: neighbor.c,v 1.11 2017/01/17 16:30:54 jca 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 /* ARGSUSED */ 324 void 325 nbr_timeout_timer(int fd, short event, void *arg) 326 { 327 struct nbr *nbr = arg; 328 329 if (nbr->state == NBR_STA_DOWN) 330 nbr_del(nbr); 331 else 332 nbr_fsm(nbr, NBR_EVT_TIMEOUT); 333 } 334 335 /* ARGSUSED */ 336 void 337 nbr_failed_timeout(int fd, short event, void *arg) 338 { 339 struct nbr_failed *nbr_failed = arg; 340 341 log_debug("nbr_failed_timeout: failed neighbor ID %s deleted", 342 inet_ntoa(nbr_failed->addr)); 343 344 nbr_failed_delete(nbr_failed); 345 } 346 347 /* actions */ 348 void 349 nbr_set_timer(struct nbr *nbr) 350 { 351 struct timeval tv; 352 353 timerclear(&tv); 354 tv.tv_sec = NBR_TIMEOUT; 355 356 if (evtimer_add(&nbr->timeout_timer, &tv) == -1) 357 fatal("nbr_set_timer"); 358 } 359 360 void 361 nbr_stop_timer(struct nbr *nbr) 362 { 363 if (evtimer_del(&nbr->timeout_timer) == -1) 364 fatal("nbr_stop_timer"); 365 } 366 367 /* names */ 368 const char * 369 nbr_event_name(int event) 370 { 371 return (nbr_event_names[event]); 372 } 373 374 const char * 375 nbr_action_name(int action) 376 { 377 return (nbr_action_names[action]); 378 } 379 380 struct ctl_nbr * 381 nbr_to_ctl(struct nbr *nbr) 382 { 383 static struct ctl_nbr nctl; 384 struct timeval tv, now, res; 385 386 memcpy(nctl.name, nbr->iface->name, sizeof(nctl.name)); 387 memcpy(&nctl.id, &nbr->id, sizeof(nctl.id)); 388 memcpy(&nctl.addr, &nbr->addr, sizeof(nctl.addr)); 389 390 nctl.nbr_state = nbr->state; 391 nctl.iface_state = nbr->iface->state; 392 393 gettimeofday(&now, NULL); 394 if (evtimer_pending(&nbr->timeout_timer, &tv)) { 395 timersub(&tv, &now, &res); 396 if (nbr->state & NBR_STA_DOWN) 397 nctl.dead_timer = NBR_TIMEOUT - res.tv_sec; 398 else 399 nctl.dead_timer = res.tv_sec; 400 } else 401 nctl.dead_timer = 0; 402 403 if (nbr->state == NBR_STA_ACTIVE) { 404 nctl.uptime = now.tv_sec - nbr->uptime; 405 } else 406 nctl.uptime = 0; 407 408 return (&nctl); 409 } 410