File: [local] / src / usr.sbin / ripd / neighbor.c (download)
Revision 1.12, Wed Mar 8 04:43:14 2023 UTC (14 months, 3 weeks ago) by guenther
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, HEAD Changes since 1.11: +1 -3 lines
Delete obsolete /* ARGSUSED */ lint comments.
ok miod@ millert@
|
/* $OpenBSD: neighbor.c,v 1.12 2023/03/08 04:43:14 guenther Exp $ */
/*
* Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
* 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 "ripd.h"
#include "rip.h"
#include "ripe.h"
#include "log.h"
#include "rde.h"
void nbr_set_timer(struct nbr *);
void nbr_stop_timer(struct nbr *);
void nbr_failed_new(struct nbr *);
void nbr_failed_timeout(int, short, void *);
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_REQUEST_RCVD, NBR_ACT_NOTHING, NBR_STA_REQ_RCVD},
{NBR_STA_DOWN, NBR_EVT_RESPONSE_RCVD, NBR_ACT_STRT_TIMER, NBR_STA_ACTIVE},
{NBR_STA_ACTIVE, NBR_EVT_RESPONSE_RCVD, NBR_ACT_RST_TIMER, NBR_STA_ACTIVE},
{NBR_STA_ACTIVE, NBR_EVT_REQUEST_RCVD, NBR_ACT_NOTHING, NBR_STA_ACTIVE},
{NBR_STA_ACTIVE, NBR_EVT_TIMEOUT, NBR_ACT_DEL, NBR_STA_DOWN},
{NBR_STA_REQ_RCVD, NBR_EVT_RESPONSE_SENT, NBR_ACT_DEL, NBR_STA_DOWN},
{NBR_STA_ACTIVE, NBR_EVT_RESPONSE_SENT, NBR_ACT_NOTHING, NBR_STA_ACTIVE},
{NBR_STA_ANY, NBR_EVT_KILL_NBR, NBR_ACT_DEL, NBR_STA_DOWN},
{-1, NBR_EVT_NOTHING, NBR_ACT_NOTHING, 0},
};
const char * const nbr_event_names[] = {
"RESPONSE RCVD",
"REQUEST RCVD",
"RESPONSE SENT",
"NBR TIMEOUT",
"NBR KILL",
"NOTHING"
};
const char * const nbr_action_names[] = {
"START TIMER",
"RESET TIMER",
"DELETE NBR",
"NOTHING"
};
int
nbr_fsm(struct nbr *nbr, enum nbr_event event)
{
struct timeval now;
int old_state;
int new_state = 0;
int i;
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) {
/* 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_TIMER:
nbr_set_timer(nbr);
break;
case NBR_ACT_STRT_TIMER:
nbr_set_timer(nbr);
break;
case NBR_ACT_DEL:
nbr_act_del(nbr);
break;
case NBR_ACT_NOTHING:
/* do nothing */
break;
}
if (new_state != 0)
nbr->state = new_state;
if (old_state != nbr->state) {
/* neighbor changed from/to ACTIVE */
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 (0);
}
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)
{
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->rp_list);
TAILQ_INIT(&nbr->rq_list);
/* set event structures */
evtimer_set(&nbr->timeout_timer, nbr_timeout_timer, nbr);
log_debug("nbr_new: neighbor ID %s, peerid %u",
inet_ntoa(nbr->id), nbr->peerid);
return (nbr);
}
void
nbr_del(struct nbr *nbr)
{
log_debug("nbr_del: neighbor ID %s, peerid %u", inet_ntoa(nbr->id),
nbr->peerid);
/* stop timer */
nbr_stop_timer(nbr);
LIST_REMOVE(nbr, entry);
LIST_REMOVE(nbr, hash);
free(nbr);
}
void
nbr_act_del(struct nbr *nbr)
{
/* If there is no authentication or it is just a route request
* there is no need to keep track of the failed neighbors */
if (nbr->iface->auth_type == AUTH_CRYPT &&
nbr->state != NBR_STA_REQ_RCVD)
nbr_failed_new(nbr);
log_debug("nbr_act_del: neighbor ID %s, peerid %u", inet_ntoa(nbr->id),
nbr->peerid);
/* schedule kill timer */
nbr_set_timer(nbr);
/* clear lists */
clear_list(&nbr->rq_list);
clear_list(&nbr->rp_list);
}
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);
}
/* failed nbr handling */
void
nbr_failed_new(struct nbr *nbr)
{
struct timeval tv;
struct iface *iface;
struct nbr_failed *nbr_failed;
if ((nbr_failed = calloc(1, sizeof(*nbr_failed))) == NULL)
fatal("nbr_failed_new");
nbr_failed->addr = nbr->addr;
nbr_failed->auth_seq_num = nbr->auth_seq_num;
iface = nbr->iface;
timerclear(&tv);
tv.tv_sec = FAILED_NBR_TIMEOUT;
evtimer_set(&nbr_failed->timeout_timer, nbr_failed_timeout,
nbr_failed);
if (evtimer_add(&nbr_failed->timeout_timer, &tv) == -1)
fatal("nbr_failed_new");
LIST_INSERT_HEAD(&iface->failed_nbr_list, nbr_failed, entry);
}
struct nbr_failed *
nbr_failed_find(struct iface *iface, u_int32_t src_ip)
{
struct nbr_failed *nbr_failed = NULL;
LIST_FOREACH(nbr_failed, &iface->failed_nbr_list, entry) {
if (nbr_failed->addr.s_addr == src_ip) {
return (nbr_failed);
}
}
return (NULL);
}
void
nbr_failed_delete(struct nbr_failed *nbr_failed)
{
if (evtimer_pending(&nbr_failed->timeout_timer, NULL))
if (evtimer_del(&nbr_failed->timeout_timer) == -1)
fatal("nbr_failed_delete");
LIST_REMOVE(nbr_failed, entry);
free(nbr_failed);
}
/* timers */
void
nbr_timeout_timer(int fd, short event, void *arg)
{
struct nbr *nbr = arg;
if (nbr->state == NBR_STA_DOWN)
nbr_del(nbr);
else
nbr_fsm(nbr, NBR_EVT_TIMEOUT);
}
void
nbr_failed_timeout(int fd, short event, void *arg)
{
struct nbr_failed *nbr_failed = arg;
log_debug("nbr_failed_timeout: failed neighbor ID %s deleted",
inet_ntoa(nbr_failed->addr));
nbr_failed_delete(nbr_failed);
}
/* actions */
void
nbr_set_timer(struct nbr *nbr)
{
struct timeval tv;
timerclear(&tv);
tv.tv_sec = NBR_TIMEOUT;
if (evtimer_add(&nbr->timeout_timer, &tv) == -1)
fatal("nbr_set_timer");
}
void
nbr_stop_timer(struct nbr *nbr)
{
if (evtimer_del(&nbr->timeout_timer) == -1)
fatal("nbr_stop_timer");
}
/* 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.nbr_state = nbr->state;
nctl.iface_state = nbr->iface->state;
gettimeofday(&now, NULL);
if (evtimer_pending(&nbr->timeout_timer, &tv)) {
timersub(&tv, &now, &res);
if (nbr->state & NBR_STA_DOWN)
nctl.dead_timer = NBR_TIMEOUT - res.tv_sec;
else
nctl.dead_timer = res.tv_sec;
} else
nctl.dead_timer = 0;
if (nbr->state == NBR_STA_ACTIVE) {
nctl.uptime = now.tv_sec - nbr->uptime;
} else
nctl.uptime = 0;
return (&nctl);
}