diff options
author | Esben Norby <norby@cvs.openbsd.org> | 2006-06-01 14:12:21 +0000 |
---|---|---|
committer | Esben Norby <norby@cvs.openbsd.org> | 2006-06-01 14:12:21 +0000 |
commit | 50b1cb4247102023b3d4274ecdef33ecc5ecf92d (patch) | |
tree | 3f03e69e83c764e467726916d53f06f34a5d7a0f /usr.sbin/dvmrpd/neighbor.c | |
parent | 7acba1e80465cf8a211886130942b49523343441 (diff) |
Welcome dvmrpd
started by Esben Norby some time ago by using the imsg/three process framework
of ospfd.
Right now it is not particularly useful, major parts are still missing but is
imported to allow more people to work on it.
Status:
The IGMP part is mostly complete, allowing clients to join and leave groups.
Election the IGMP querier of a network is also functional, only thing missing is
some corner cases when going from non-querier to querier.
About half of the DVMRP is functional, probes and route reports are functional.
Multicast streams can be detected and the MFC can be manipulated.
The RIB is not complete but operational.
Not yet connected to the builds.
OK claudio@
Diffstat (limited to 'usr.sbin/dvmrpd/neighbor.c')
-rw-r--r-- | usr.sbin/dvmrpd/neighbor.c | 427 |
1 files changed, 427 insertions, 0 deletions
diff --git a/usr.sbin/dvmrpd/neighbor.c b/usr.sbin/dvmrpd/neighbor.c new file mode 100644 index 00000000000..c8282c5eaa2 --- /dev/null +++ b/usr.sbin/dvmrpd/neighbor.c @@ -0,0 +1,427 @@ +/* $OpenBSD: neighbor.c,v 1.1 2006/06/01 14:12:20 norby Exp $ */ + +/* + * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> + +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <event.h> + +#include "igmp.h" +#include "dvmrpd.h" +#include "dvmrp.h" +#include "dvmrpe.h" +#include "log.h" +#include "rde.h" + +LIST_HEAD(nbr_head, nbr); + +struct nbr_table { + struct nbr_head *hashtbl; + u_int32_t hashmask; +} nbrtable; + +#define NBR_HASH(x) \ + &nbrtable.hashtbl[(x) & nbrtable.hashmask] + +u_int32_t peercnt = NBR_CNTSTART; + +struct { + int state; + enum nbr_event event; + enum nbr_action action; + int new_state; +} nbr_fsm_tbl[] = { + /* current state event that happened action to take resulting state */ + {NBR_STA_DOWN, NBR_EVT_PROBE_RCVD, NBR_ACT_STRT_ITIMER, NBR_STA_1_WAY}, + {NBR_STA_ACTIVE, NBR_EVT_PROBE_RCVD, NBR_ACT_RST_ITIMER, 0}, + {NBR_STA_1_WAY, NBR_EVT_2_WAY_RCVD, NBR_ACT_STRT_ITIMER, NBR_STA_2_WAY}, + {NBR_STA_ACTIVE, NBR_EVT_1_WAY_RCVD, NBR_ACT_RESET, NBR_STA_1_WAY}, + {NBR_STA_ANY, NBR_EVT_KILL_NBR, NBR_ACT_DEL, NBR_STA_DOWN}, + {NBR_STA_ANY, NBR_EVT_LL_DOWN, NBR_ACT_DEL, NBR_STA_DOWN}, + {NBR_STA_ANY, NBR_EVT_ITIMER, NBR_ACT_DEL, NBR_STA_DOWN}, + {-1, NBR_EVT_NOTHING, NBR_ACT_NOTHING, 0}, +}; + +const char * const nbr_event_names[] = { + "NOTHING", + "PROBE RCVD", + "1-WAY RCVD", + "2-WAY RCVD", + "KILL NBR", + "ITIMER", + "LL DOWN" +}; + +const char * const nbr_action_names[] = { + "NOTHING", + "RESET ITIMER", + "START ITIMER", + "RESET", + "DELETE", + "CLEAR LISTS" +}; + +int +nbr_fsm(struct nbr *nbr, enum nbr_event event) +{ + struct timeval now; + int old_state; + int new_state = 0; + int i, ret = 0; + + old_state = nbr->state; + for (i = 0; nbr_fsm_tbl[i].state != -1; i++) + if ((nbr_fsm_tbl[i].state & old_state) && + (nbr_fsm_tbl[i].event == event)) { + new_state = nbr_fsm_tbl[i].new_state; + break; + } + + if (nbr_fsm_tbl[i].state == -1) { + /* XXX event outside of the defined fsm, ignore it. */ + log_warnx("nbr_fsm: neighbor ID %s, " + "event '%s' not expected in state '%s'", + inet_ntoa(nbr->id), nbr_event_name(event), + nbr_state_name(old_state)); + return (0); + } + + switch (nbr_fsm_tbl[i].action) { + case NBR_ACT_RST_ITIMER: + ret = nbr_act_reset_itimer(nbr); + break; + case NBR_ACT_STRT_ITIMER: + ret = nbr_act_start_itimer(nbr); + break; + case NBR_ACT_RESET: + /* XXX nbr action reset */ + break; + case NBR_ACT_DEL: + ret = nbr_act_delete(nbr); + break; + case NBR_ACT_CLR_LST: + ret = nbr_act_clear_lists(nbr); + break; + case NBR_ACT_NOTHING: + /* do nothing */ + break; + } + + if (ret) { + log_warnx("nbr_fsm: error changing state for neighbor ID %s, " + "event '%s', state '%s'", inet_ntoa(nbr->id), + nbr_event_name(event), nbr_state_name(old_state)); + return (-1); + } + + if (new_state != 0) + nbr->state = new_state; + + if (old_state != nbr->state) { + if (old_state & NBR_STA_2_WAY || nbr->state & NBR_STA_2_WAY) { + /* neighbor changed from/to 2_WAY */ + + gettimeofday(&now, NULL); + nbr->uptime = now.tv_sec; + } + + log_debug("nbr_fsm: event '%s' resulted in action '%s' and " + "changing state for neighbor ID %s from '%s' to '%s'", + nbr_event_name(event), + nbr_action_name(nbr_fsm_tbl[i].action), + inet_ntoa(nbr->id), nbr_state_name(old_state), + nbr_state_name(nbr->state)); + } + + return (ret); +} + +void +nbr_init(u_int32_t hashsize) +{ + u_int32_t hs, i; + + for (hs = 1; hs < hashsize; hs <<= 1) + ; + nbrtable.hashtbl = calloc(hs, sizeof(struct nbr_head)); + if (nbrtable.hashtbl == NULL) + fatal("nbr_init"); + + for (i = 0; i < hs; i++) + LIST_INIT(&nbrtable.hashtbl[i]); + + nbrtable.hashmask = hs - 1; +} + +struct nbr * +nbr_new(u_int32_t nbr_id, struct iface *iface, int self) +{ + struct nbr_head *head; + struct nbr *nbr = NULL; + + if ((nbr = calloc(1, sizeof(*nbr))) == NULL) + fatal("nbr_new"); + + nbr->state = NBR_STA_DOWN; + nbr->id.s_addr = nbr_id; + + /* get next unused peerid */ + while (nbr_find_peerid(++peercnt)) + ; + nbr->peerid = peercnt; + head = NBR_HASH(nbr->peerid); + LIST_INSERT_HEAD(head, nbr, hash); + + /* add to peer list */ + nbr->iface = iface; + LIST_INSERT_HEAD(&iface->nbr_list, nbr, entry); + + TAILQ_INIT(&nbr->rr_list); + + /* set event structures */ + evtimer_set(&nbr->inactivity_timer, nbr_itimer, nbr); + + log_debug("nbr_new: neighbor ID %s, peerid %lu", + inet_ntoa(nbr->id), nbr->peerid); + + return (nbr); +} + +int +nbr_del(struct nbr *nbr) +{ + log_debug("nbr_del: neighbor ID %s, peerid %lu", inet_ntoa(nbr->id), + nbr->peerid); + + /* clear lists */ + rr_list_clr(&nbr->rr_list); + + LIST_REMOVE(nbr, entry); + LIST_REMOVE(nbr, hash); + + free(nbr); + + return (0); +} + +struct nbr * +nbr_find_peerid(u_int32_t peerid) +{ + struct nbr_head *head; + struct nbr *nbr; + + head = NBR_HASH(peerid); + + LIST_FOREACH(nbr, head, hash) { + if (nbr->peerid == peerid) + return (nbr); + } + + return (NULL); +} + +struct nbr * +nbr_find_ip(struct iface *iface, u_int32_t src_ip) +{ + struct nbr *nbr = NULL; + + LIST_FOREACH(nbr, &iface->nbr_list, entry) { + if (nbr->id.s_addr == src_ip) { + return (nbr); + } + } + + return (NULL); +} + +/* timers */ +void +nbr_itimer(int fd, short event, void *arg) +{ + struct nbr *nbr = arg; + + log_debug("nbr_itimer: %s", inet_ntoa(nbr->id)); + + if (nbr->state == NBR_STA_DOWN) { + nbr_del(nbr); + } else + nbr_fsm(nbr, NBR_EVT_ITIMER); +} + +int +nbr_start_itimer(struct nbr *nbr) +{ + struct timeval tv; + + log_debug("nbr_start_itimer: %s", inet_ntoa(nbr->id)); + + timerclear(&tv); + tv.tv_sec = nbr->iface->dead_interval; + + return (evtimer_add(&nbr->inactivity_timer, &tv)); +} + +int +nbr_stop_itimer(struct nbr *nbr) +{ + return (evtimer_del(&nbr->inactivity_timer)); +} + +int +nbr_reset_itimer(struct nbr *nbr) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = nbr->iface->dead_interval; + + return (evtimer_add(&nbr->inactivity_timer, &tv)); +} + +/* actions */ +int +nbr_act_start(struct nbr *nbr) +{ + log_debug("nbr_act_start: neighbor ID %s", inet_ntoa(nbr->id)); + + return (-1); +} + +int +nbr_act_reset_itimer(struct nbr *nbr) +{ + if (nbr_reset_itimer(nbr)) { + log_warnx("nbr_act_reset_itimer: cannot schedule inactivity " + "timer, neighbor ID %s", inet_ntoa(nbr->id)); + return (-1); + } + + return (0); +} + +int +nbr_act_start_itimer(struct nbr *nbr) +{ + if (nbr_start_itimer(nbr)) { + log_warnx("nbr_act_start_itimer: cannot schedule inactivity " + "timer, neighbor ID %s", + inet_ntoa(nbr->id)); + return (-1); + } + + if (nbr->state == NBR_STA_1_WAY) { + /* new nbr, send entire route table, unicast */ + log_debug("nbr_act_start_itimer: nbr %s, send route table", + inet_ntoa(nbr->id)); + + dvmrpe_imsg_compose_rde(IMSG_FULL_ROUTE_REPORT, nbr->peerid, 0, + NULL, 0); + } + + return (0); +} + +int +nbr_act_delete(struct nbr *nbr) +{ + struct timeval tv; + + log_debug("nbr_act_delete: neighbor ID %s", inet_ntoa(nbr->id)); + + /* stop timers */ + if (nbr_stop_itimer(nbr)) { + log_warnx("nbr_act_delete: error removing inactivity timer, " + "neighbor ID %s", inet_ntoa(nbr->id)); + return (-1); + } + + /* schedule kill timer */ + timerclear(&tv); + tv.tv_sec = DEFAULT_NBR_TMOUT; + + if (evtimer_add(&nbr->inactivity_timer, &tv)) { + log_warnx("nbr_act_delete: error scheduling neighbor ID %s " + "for removal", inet_ntoa(nbr->id)); + } + + return (nbr_act_clear_lists(nbr)); +} + +int +nbr_act_clear_lists(struct nbr *nbr) +{ + log_debug("nbr_act_clear_lists: neighbor ID %s", inet_ntoa(nbr->id)); + + /* XXX clear lists */ + + return (0); +} + +/* names */ +const char * +nbr_event_name(int event) +{ + return (nbr_event_names[event]); +} + +const char * +nbr_action_name(int action) +{ + return (nbr_action_names[action]); +} + +struct ctl_nbr * +nbr_to_ctl(struct nbr *nbr) +{ + static struct ctl_nbr nctl; + struct timeval tv, now, res; + + memcpy(nctl.name, nbr->iface->name, sizeof(nctl.name)); + memcpy(&nctl.id, &nbr->id, sizeof(nctl.id)); + memcpy(&nctl.addr, &nbr->addr, sizeof(nctl.addr)); + + nctl.state = nbr->state; + + gettimeofday(&now, NULL); + if (evtimer_pending(&nbr->inactivity_timer, &tv)) { + timersub(&tv, &now, &res); + if (nbr->state & NBR_STA_DOWN) + nctl.dead_timer = DEFAULT_NBR_TMOUT - res.tv_sec; + else + nctl.dead_timer = res.tv_sec; + } else + nctl.dead_timer = 0; + + if (nbr->state == NBR_STA_2_WAY) { + nctl.uptime = now.tv_sec - nbr->uptime; + } else + nctl.uptime = 0; + + return (&nctl); +} |