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 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 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 * 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 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 * 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 * 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 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 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 289 nbr_stop_itimer(struct nbr *nbr) 290 { 291 return (evtimer_del(&nbr->inactivity_timer)); 292 } 293 294 int 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 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 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 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 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 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 * 381 nbr_event_name(int event) 382 { 383 return (nbr_event_names[event]); 384 } 385 386 const char * 387 nbr_action_name(int action) 388 { 389 return (nbr_action_names[action]); 390 } 391 392 struct ctl_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