| File: | src/usr.sbin/dvmrpd/neighbor.c |
| Warning: | line 145, column 19 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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)struct nbr_head { struct nbr *lh_first; }; | |||
| 43 | ||||
| 44 | struct nbr_table { | |||
| 45 | struct nbr_head *hashtbl; | |||
| 46 | u_int32_t hashmask; | |||
| 47 | } nbrtable; | |||
| 48 | ||||
| 49 | #define NBR_HASH(x)&nbrtable.hashtbl[(x) & nbrtable.hashmask] \ | |||
| 50 | &nbrtable.hashtbl[(x) & nbrtable.hashmask] | |||
| 51 | ||||
| 52 | u_int32_t peercnt = NBR_CNTSTART(1 + 1); | |||
| 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_DOWN0x01, NBR_EVT_PROBE_RCVD, NBR_ACT_STRT_ITIMER, NBR_STA_1_WAY0x02}, | |||
| 62 | {NBR_STA_ACTIVE(~0x01), NBR_EVT_PROBE_RCVD, NBR_ACT_RST_ITIMER, 0}, | |||
| 63 | {NBR_STA_1_WAY0x02, NBR_EVT_2_WAY_RCVD, NBR_ACT_STRT_ITIMER, NBR_STA_2_WAY0x04}, | |||
| 64 | {NBR_STA_ACTIVE(~0x01), NBR_EVT_1_WAY_RCVD, NBR_ACT_RESET, NBR_STA_1_WAY0x02}, | |||
| 65 | {NBR_STA_ANY0xff, NBR_EVT_KILL_NBR, NBR_ACT_DEL, NBR_STA_DOWN0x01}, | |||
| 66 | {NBR_STA_ANY0xff, NBR_EVT_ITIMER, NBR_ACT_DEL, NBR_STA_DOWN0x01}, | |||
| 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_WAY0x04 || nbr->state & NBR_STA_2_WAY0x04) { | |||
| 147 | /* neighbor changed from/to 2_WAY */ | |||
| 148 | ||||
| 149 | gettimeofday(&now, NULL((void *)0)); | |||
| 150 | nbr->uptime = now.tv_sec; | |||
| 151 | ||||
| 152 | if (nbr->state & NBR_STA_2_WAY0x04) | |||
| 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((void *)0)) | |||
| 178 | fatal("nbr_init"); | |||
| 179 | ||||
| 180 | for (i = 0; i < hs; i++) | |||
| 181 | LIST_INIT(&nbrtable.hashtbl[i])do { ((&nbrtable.hashtbl[i])->lh_first) = ((void *)0); } while (0); | |||
| 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((void *)0); | |||
| 191 | ||||
| 192 | if ((nbr = calloc(1, sizeof(*nbr))) == NULL((void *)0)) | |||
| 193 | fatal("nbr_new"); | |||
| 194 | ||||
| 195 | nbr->state = NBR_STA_DOWN0x01; | |||
| 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)&nbrtable.hashtbl[(nbr->peerid) & nbrtable.hashmask ]; | |||
| 203 | LIST_INSERT_HEAD(head, nbr, hash)do { if (((nbr)->hash.le_next = (head)->lh_first) != (( void *)0)) (head)->lh_first->hash.le_prev = &(nbr)-> hash.le_next; (head)->lh_first = (nbr); (nbr)->hash.le_prev = &(head)->lh_first; } while (0); | |||
| 204 | ||||
| 205 | /* add to peer list */ | |||
| 206 | nbr->iface = iface; | |||
| 207 | LIST_INSERT_HEAD(&iface->nbr_list, nbr, entry)do { if (((nbr)->entry.le_next = (&iface->nbr_list) ->lh_first) != ((void *)0)) (&iface->nbr_list)-> lh_first->entry.le_prev = &(nbr)->entry.le_next; (& iface->nbr_list)->lh_first = (nbr); (nbr)->entry.le_prev = &(&iface->nbr_list)->lh_first; } while (0); | |||
| 208 | ||||
| 209 | TAILQ_INIT(&nbr->rr_list)do { (&nbr->rr_list)->tqh_first = ((void *)0); (& nbr->rr_list)->tqh_last = &(&nbr->rr_list)-> tqh_first; } while (0); | |||
| 210 | ||||
| 211 | /* set event structures */ | |||
| 212 | evtimer_set(&nbr->inactivity_timer, nbr_itimer, nbr)event_set(&nbr->inactivity_timer, -1, 0, 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)do { if ((nbr)->entry.le_next != ((void *)0)) (nbr)->entry .le_next->entry.le_prev = (nbr)->entry.le_prev; *(nbr)-> entry.le_prev = (nbr)->entry.le_next; ; ; } while (0); | |||
| 227 | LIST_REMOVE(nbr, hash)do { if ((nbr)->hash.le_next != ((void *)0)) (nbr)->hash .le_next->hash.le_prev = (nbr)->hash.le_prev; *(nbr)-> hash.le_prev = (nbr)->hash.le_next; ; ; } while (0); | |||
| 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)&nbrtable.hashtbl[(peerid) & nbrtable.hashmask]; | |||
| 241 | ||||
| 242 | LIST_FOREACH(nbr, head, hash)for((nbr) = ((head)->lh_first); (nbr)!= ((void *)0); (nbr) = ((nbr)->hash.le_next)) { | |||
| 243 | if (nbr->peerid == peerid) | |||
| 244 | return (nbr); | |||
| 245 | } | |||
| 246 | ||||
| 247 | return (NULL((void *)0)); | |||
| 248 | } | |||
| 249 | ||||
| 250 | struct nbr * | |||
| 251 | nbr_find_ip(struct iface *iface, u_int32_t src_ip) | |||
| 252 | { | |||
| 253 | struct nbr *nbr = NULL((void *)0); | |||
| 254 | ||||
| 255 | LIST_FOREACH(nbr, &iface->nbr_list, entry)for((nbr) = ((&iface->nbr_list)->lh_first); (nbr)!= ((void *)0); (nbr) = ((nbr)->entry.le_next)) { | |||
| 256 | if (nbr->id.s_addr == src_ip) { | |||
| 257 | return (nbr); | |||
| 258 | } | |||
| 259 | } | |||
| 260 | ||||
| 261 | return (NULL((void *)0)); | |||
| 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)(&tv)->tv_sec = (&tv)->tv_usec = 0; | |||
| 283 | tv.tv_sec = nbr->iface->dead_interval; | |||
| 284 | ||||
| 285 | return (evtimer_add(&nbr->inactivity_timer, &tv)event_add(&nbr->inactivity_timer, &tv)); | |||
| 286 | } | |||
| 287 | ||||
| 288 | int | |||
| 289 | nbr_stop_itimer(struct nbr *nbr) | |||
| 290 | { | |||
| 291 | return (evtimer_del(&nbr->inactivity_timer)event_del(&nbr->inactivity_timer)); | |||
| 292 | } | |||
| 293 | ||||
| 294 | int | |||
| 295 | nbr_reset_itimer(struct nbr *nbr) | |||
| 296 | { | |||
| 297 | struct timeval tv; | |||
| 298 | ||||
| 299 | timerclear(&tv)(&tv)->tv_sec = (&tv)->tv_usec = 0; | |||
| 300 | tv.tv_sec = nbr->iface->dead_interval; | |||
| 301 | ||||
| 302 | return (evtimer_add(&nbr->inactivity_timer, &tv)event_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_WAY0x02) { | |||
| 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((void *)0), 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((void *)0)); | |||
| 405 | if (evtimer_pending(&nbr->inactivity_timer, &tv)event_pending(&nbr->inactivity_timer, 0x01, &tv)) { | |||
| 406 | timersub(&tv, &now, &res)do { (&res)->tv_sec = (&tv)->tv_sec - (&now )->tv_sec; (&res)->tv_usec = (&tv)->tv_usec - (&now)->tv_usec; if ((&res)->tv_usec < 0) { (&res)->tv_sec--; (&res)->tv_usec += 1000000; } } while (0); | |||
| 407 | nctl.dead_timer = res.tv_sec; | |||
| 408 | } else | |||
| 409 | nctl.dead_timer = 0; | |||
| 410 | ||||
| 411 | if (nbr->state == NBR_STA_2_WAY0x04) { | |||
| 412 | nctl.uptime = now.tv_sec - nbr->uptime; | |||
| 413 | } else | |||
| 414 | nctl.uptime = 0; | |||
| 415 | ||||
| 416 | return (&nctl); | |||
| 417 | } |