1 /* $OpenBSD: neighbor.c,v 1.9 2007/10/24 19:50:33 claudio 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 %lu", 200 inet_ntoa(nbr->id), nbr->peerid); 201 202 return (nbr); 203 } 204 205 void 206 nbr_act_del(struct nbr *nbr) 207 { 208 /* If there is no authentication or it is just a route request 209 * there is no need to keep track of the failed neighbors */ 210 if (nbr->iface->auth_type == AUTH_CRYPT && 211 nbr->state != NBR_STA_REQ_RCVD) 212 nbr_failed_new(nbr); 213 214 log_debug("nbr_del: neighbor ID %s, peerid %lu", inet_ntoa(nbr->id), 215 nbr->peerid); 216 217 /* stop timer */ 218 nbr_stop_timer(nbr); 219 220 /* clear lists */ 221 clear_list(&nbr->rq_list); 222 clear_list(&nbr->rp_list); 223 224 LIST_REMOVE(nbr, entry); 225 LIST_REMOVE(nbr, hash); 226 227 free(nbr); 228 } 229 230 struct nbr * 231 nbr_find_peerid(u_int32_t peerid) 232 { 233 struct nbr_head *head; 234 struct nbr *nbr; 235 236 head = NBR_HASH(peerid); 237 238 LIST_FOREACH(nbr, head, hash) { 239 if (nbr->peerid == peerid) 240 return (nbr); 241 } 242 243 return (NULL); 244 } 245 246 struct nbr * 247 nbr_find_ip(struct iface *iface, u_int32_t src_ip) 248 { 249 struct nbr *nbr = NULL; 250 251 LIST_FOREACH(nbr, &iface->nbr_list, entry) { 252 if (nbr->id.s_addr == src_ip) { 253 return (nbr); 254 } 255 } 256 257 return (NULL); 258 } 259 260 /* failed nbr handling */ 261 void 262 nbr_failed_new(struct nbr *nbr) 263 { 264 struct timeval tv; 265 struct iface *iface; 266 struct nbr_failed *nbr_failed; 267 268 if ((nbr_failed = calloc(1, sizeof(*nbr_failed))) == NULL) 269 fatal("nbr_failed_new"); 270 271 nbr_failed->addr = nbr->addr; 272 nbr_failed->auth_seq_num = nbr->auth_seq_num; 273 iface = nbr->iface; 274 275 timerclear(&tv); 276 tv.tv_sec = FAILED_NBR_TIMEOUT; 277 278 evtimer_set(&nbr_failed->timeout_timer, nbr_failed_timeout, 279 nbr_failed); 280 281 if (evtimer_add(&nbr_failed->timeout_timer, &tv) == -1) 282 fatal("nbr_failed_new"); 283 284 LIST_INSERT_HEAD(&iface->failed_nbr_list, nbr_failed, entry); 285 } 286 287 struct nbr_failed * 288 nbr_failed_find(struct iface *iface, u_int32_t src_ip) 289 { 290 struct nbr_failed *nbr_failed = NULL; 291 292 LIST_FOREACH(nbr_failed, &iface->failed_nbr_list, entry) { 293 if (nbr_failed->addr.s_addr == src_ip) { 294 return (nbr_failed); 295 } 296 } 297 298 return (NULL); 299 } 300 301 void 302 nbr_failed_delete(struct nbr_failed *nbr_failed) 303 { 304 if (evtimer_pending(&nbr_failed->timeout_timer, NULL)) 305 if (evtimer_del(&nbr_failed->timeout_timer) == -1) 306 fatal("nbr_failed_delete"); 307 308 LIST_REMOVE(nbr_failed, entry); 309 free(nbr_failed); 310 } 311 312 /* timers */ 313 /* ARGSUSED */ 314 void 315 nbr_timeout_timer(int fd, short event, void *arg) 316 { 317 struct nbr *nbr = arg; 318 319 nbr_fsm(nbr, NBR_EVT_TIMEOUT); 320 } 321 322 /* ARGSUSED */ 323 void 324 nbr_failed_timeout(int fd, short event, void *arg) 325 { 326 struct nbr_failed *nbr_failed = arg; 327 328 log_debug("nbr_failed_timeout: failed neighbor ID %s deleted", 329 inet_ntoa(nbr_failed->addr)); 330 331 nbr_failed_delete(nbr_failed); 332 } 333 334 /* actions */ 335 void 336 nbr_set_timer(struct nbr *nbr) 337 { 338 struct timeval tv; 339 340 timerclear(&tv); 341 tv.tv_sec = NBR_TIMEOUT; 342 343 if (evtimer_add(&nbr->timeout_timer, &tv) == -1) 344 fatal("nbr_set_timer"); 345 } 346 347 void 348 nbr_stop_timer(struct nbr *nbr) 349 { 350 if (evtimer_del(&nbr->timeout_timer) == -1) 351 fatal("nbr_stop_timer"); 352 } 353 354 /* names */ 355 const char * 356 nbr_event_name(int event) 357 { 358 return (nbr_event_names[event]); 359 } 360 361 const char * 362 nbr_action_name(int action) 363 { 364 return (nbr_action_names[action]); 365 } 366 367 struct ctl_nbr * 368 nbr_to_ctl(struct nbr *nbr) 369 { 370 static struct ctl_nbr nctl; 371 struct timeval tv, now, res; 372 373 memcpy(nctl.name, nbr->iface->name, sizeof(nctl.name)); 374 memcpy(&nctl.id, &nbr->id, sizeof(nctl.id)); 375 memcpy(&nctl.addr, &nbr->addr, sizeof(nctl.addr)); 376 377 nctl.nbr_state = nbr->state; 378 nctl.iface_state = nbr->iface->state; 379 380 gettimeofday(&now, NULL); 381 if (evtimer_pending(&nbr->timeout_timer, &tv)) { 382 timersub(&tv, &now, &res); 383 if (nbr->state & NBR_STA_DOWN) 384 nctl.dead_timer = NBR_TIMEOUT - res.tv_sec; 385 else 386 nctl.dead_timer = res.tv_sec; 387 } else 388 nctl.dead_timer = 0; 389 390 if (nbr->state == NBR_STA_ACTIVE) { 391 nctl.uptime = now.tv_sec - nbr->uptime; 392 } else 393 nctl.uptime = 0; 394 395 return (&nctl); 396 } 397