[BACK]Return to neighbor.c CVS log [TXT][DIR] Up to [local] / src / usr.sbin / eigrpd

File: [local] / src / usr.sbin / eigrpd / neighbor.c (download)

Revision 1.11, Wed Mar 8 04:43:13 2023 UTC (15 months 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.10: +1 -2 lines

Delete obsolete /* ARGSUSED */ lint comments.

ok miod@ millert@

/*	$OpenBSD: neighbor.c,v 1.11 2023/03/08 04:43:13 guenther Exp $ */

/*
 * Copyright (c) 2015 Renato Westphal <renato@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 <string.h>
#include <stdlib.h>

#include "eigrpd.h"
#include "eigrpe.h"
#include "rde.h"
#include "log.h"

static __inline int	 nbr_compare(struct nbr *, struct nbr *);
static __inline int	 nbr_pid_compare(struct nbr *, struct nbr *);
static void		 nbr_update_peerid(struct nbr *);
static void		 nbr_timeout(int, short, void *);
static void		 nbr_stop_timeout(struct nbr *);

RB_GENERATE(nbr_addr_head, nbr, addr_tree, nbr_compare)
RB_GENERATE(nbr_pid_head, nbr, pid_tree, nbr_pid_compare)

struct nbr_pid_head nbrs_by_pid = RB_INITIALIZER(&nbrs_by_pid);

static __inline int
nbr_compare(struct nbr *a, struct nbr *b)
{
	if (a->ei->iface->ifindex < b->ei->iface->ifindex)
		return (-1);
	if (a->ei->iface->ifindex > b->ei->iface->ifindex)
		return (1);

	return (eigrp_addrcmp(a->ei->eigrp->af, &a->addr, &b->addr));
}

static __inline int
nbr_pid_compare(struct nbr *a, struct nbr *b)
{
	return (a->peerid - b->peerid);
}

struct nbr *
nbr_new(struct eigrp_iface *ei, union eigrpd_addr *addr, uint16_t holdtime,
    int self)
{
	struct eigrp		*eigrp = ei->eigrp;
	struct nbr		*nbr;

	if (!self)
		log_debug("%s: interface %s addr %s as %u", __func__,
		    ei->iface->name, log_addr(eigrp->af, addr), eigrp->as);

	if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
		fatal("nbr_new");

	nbr->ei = ei;
	TAILQ_INSERT_TAIL(&ei->nbr_list, nbr, entry);
	nbr->addr = *addr;
	nbr->peerid = 0;
	nbr->hello_holdtime = holdtime;
	nbr->flags = F_EIGRP_NBR_PENDING;
	if (self)
		nbr->flags |= F_EIGRP_NBR_SELF;
	TAILQ_INIT(&nbr->update_list);
	TAILQ_INIT(&nbr->query_list);
	TAILQ_INIT(&nbr->reply_list);
	TAILQ_INIT(&nbr->retrans_list);

	if (RB_INSERT(nbr_addr_head, &eigrp->nbrs, nbr) != NULL)
		fatalx("nbr_new: RB_INSERT(eigrp->nbrs) failed");

	/* timeout handling */
	if (!self) {
		evtimer_set(&nbr->ev_ack, rtp_ack_timer, nbr);
		evtimer_set(&nbr->ev_hello_timeout, nbr_timeout, nbr);
		nbr_start_timeout(nbr);
	}

	return (nbr);
}

void
nbr_init(struct nbr *nbr)
{
	struct timeval		 now;
	struct rde_nbr		 rnbr;

	nbr->flags &= ~F_EIGRP_NBR_PENDING;

	gettimeofday(&now, NULL);
	nbr->uptime = now.tv_sec;

	nbr_update_peerid(nbr);

	memset(&rnbr, 0, sizeof(rnbr));
	rnbr.addr = nbr->addr;
	rnbr.ifaceid = nbr->ei->ifaceid;
	if (nbr->flags & F_EIGRP_NBR_SELF)
		rnbr.flags = F_RDE_NBR_SELF|F_RDE_NBR_LOCAL;

	/* rde is not aware of pending nbrs */
	eigrpe_imsg_compose_rde(IMSG_NEIGHBOR_UP, nbr->peerid, 0, &rnbr,
	    sizeof(rnbr));
}

void
nbr_del(struct nbr *nbr)
{
	struct eigrp		*eigrp = nbr->ei->eigrp;
	struct packet		*pkt;

	if (!(nbr->flags & F_EIGRP_NBR_SELF))
		log_debug("%s: addr %s", __func__,
		    log_addr(eigrp->af, &nbr->addr));

	eigrpe_imsg_compose_rde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, NULL, 0);

	nbr_stop_timeout(nbr);

	/* clear retransmission list */
	while ((pkt = TAILQ_FIRST(&nbr->retrans_list)) != NULL)
		rtp_packet_del(pkt);

	if (nbr->peerid)
		RB_REMOVE(nbr_pid_head, &nbrs_by_pid, nbr);
	RB_REMOVE(nbr_addr_head, &eigrp->nbrs, nbr);
	TAILQ_REMOVE(&nbr->ei->nbr_list, nbr, entry);

	free(nbr);
}

static void
nbr_update_peerid(struct nbr *nbr)
{
	static uint32_t	 peercnt = NBR_CNTSTART;

	if (nbr->peerid)
		RB_REMOVE(nbr_pid_head, &nbrs_by_pid, nbr);

	/* get next unused peerid */
	while (nbr_find_peerid(++peercnt))
		;
	nbr->peerid = peercnt;

	if (RB_INSERT(nbr_pid_head, &nbrs_by_pid, nbr) != NULL)
		fatalx("nbr_new: RB_INSERT(nbrs_by_pid) failed");
}

struct nbr *
nbr_find(struct eigrp_iface *ei, union eigrpd_addr *addr)
{
	struct nbr		 n;
	struct eigrp_iface	 i;
	struct eigrp		 e;

	e.af = ei->eigrp->af;
	e.as = ei->eigrp->as;
	i.eigrp = &e;
	i.iface = ei->iface;
	n.ei = &i;
	n.addr = *addr;

	return (RB_FIND(nbr_addr_head, &ei->eigrp->nbrs, &n));
}

struct nbr *
nbr_find_peerid(uint32_t peerid)
{
	struct nbr	n;
	n.peerid = peerid;
	return (RB_FIND(nbr_pid_head, &nbrs_by_pid, &n));
}

struct ctl_nbr *
nbr_to_ctl(struct nbr *nbr)
{
	static struct ctl_nbr	 nctl;
	struct timeval		 now;

	nctl.af = nbr->ei->eigrp->af;
	nctl.as = nbr->ei->eigrp->as;
	memcpy(nctl.ifname, nbr->ei->iface->name, sizeof(nctl.ifname));
	nctl.addr = nbr->addr;
	nctl.hello_holdtime = nbr->hello_holdtime;
	gettimeofday(&now, NULL);
	nctl.uptime = now.tv_sec - nbr->uptime;

	return (&nctl);
}

void
nbr_clear_ctl(struct ctl_nbr *nctl)
{
	struct eigrp		*eigrp;
	struct nbr		*nbr, *safe;

	TAILQ_FOREACH(eigrp, &econf->instances, entry) {
		if (nctl->af && nctl->af != eigrp->af)
			continue;
		if (nctl->as && nctl->as != eigrp->as)
			continue;

		RB_FOREACH_SAFE(nbr, nbr_addr_head, &eigrp->nbrs, safe) {
			if (nbr->flags & (F_EIGRP_NBR_PENDING|F_EIGRP_NBR_SELF))
				continue;
			if (eigrp_addrisset(nctl->af, &nctl->addr) &&
			    eigrp_addrcmp(nctl->af, &nctl->addr, &nbr->addr))
				continue;

			log_debug("%s: neighbor %s manually cleared", __func__,
			    log_addr(nbr->ei->eigrp->af, &nbr->addr));
			send_peerterm(nbr);
			nbr_del(nbr);
		}
	}
}

/* timers */

static void
nbr_timeout(int fd, short event, void *arg)
{
	struct nbr	*nbr = arg;
	struct eigrp	*eigrp = nbr->ei->eigrp;

	log_debug("%s: neighbor %s", __func__, log_addr(eigrp->af, &nbr->addr));

	nbr_del(nbr);
}

void
nbr_start_timeout(struct nbr *nbr)
{
	struct timeval	tv;

	timerclear(&tv);
	tv.tv_sec = nbr->hello_holdtime;

	if (evtimer_add(&nbr->ev_hello_timeout, &tv) == -1)
		fatal("nbr_start_timeout");
}

static void
nbr_stop_timeout(struct nbr *nbr)
{
	if (evtimer_pending(&nbr->ev_hello_timeout, NULL) &&
	    evtimer_del(&nbr->ev_hello_timeout) == -1)
		fatal("nbr_stop_timeout");
}