#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "eigrpd.h"
#include "eigrpe.h"
#include "rde.h"
#include "log.h"
static int dual_fsm(struct rt_node *, enum dual_event);
static __inline int rt_compare(struct rt_node *, struct rt_node *);
static struct rt_node *rt_find(struct eigrp *, struct rinfo *);
static struct rt_node *rt_new(struct eigrp *, struct rinfo *);
static struct eigrp_route *route_find(struct rde_nbr *, struct rt_node *);
static struct eigrp_route *route_new(struct rt_node *, struct rde_nbr *,
struct rinfo *);
static void route_del(struct rt_node *, struct eigrp_route *);
static uint32_t safe_sum_uint32(uint32_t, uint32_t);
static uint32_t safe_mul_uint32(uint32_t, uint32_t);
static uint32_t route_composite_metric(uint8_t *, uint32_t, uint32_t,
uint8_t, uint8_t);
static void route_update_metrics(struct eigrp *,
struct eigrp_route *, struct rinfo *);
static void reply_outstanding_add(struct rt_node *,
struct rde_nbr *);
static struct reply_node *reply_outstanding_find(struct rt_node *,
struct rde_nbr *);
static void reply_outstanding_remove(struct reply_node *);
static void reply_active_timer(int, short, void *);
static void reply_active_start_timer(struct reply_node *);
static void reply_active_stop_timer(struct reply_node *);
static void reply_sia_timer(int, short, void *);
static void reply_sia_start_timer(struct reply_node *);
static void reply_sia_stop_timer(struct reply_node *);
static void rinfo_fill_infinite(struct rt_node *, enum route_type,
struct rinfo *);
static void rt_update_fib(struct rt_node *);
static void rt_set_successor(struct rt_node *,
struct eigrp_route *);
static struct eigrp_route *rt_get_successor_fc(struct rt_node *);
static void rde_send_update(struct eigrp_iface *, struct rinfo *);
static void rde_send_update_all(struct rt_node *, struct rinfo *);
static void rde_send_query(struct eigrp_iface *, struct rinfo *,
int);
static void rde_send_siaquery(struct rde_nbr *, struct rinfo *);
static void rde_send_query_all(struct eigrp *, struct rt_node *,
int);
static void rde_send_reply(struct rde_nbr *, struct rinfo *, int);
static void rde_last_reply(struct rt_node *);
static __inline int rde_nbr_compare(struct rde_nbr *, struct rde_nbr *);
RB_GENERATE(rt_tree, rt_node, entry, rt_compare)
RB_GENERATE(rde_nbr_head, rde_nbr, entry, rde_nbr_compare)
struct rde_nbr_head rde_nbrs = RB_INITIALIZER(&rde_nbrs);
static struct {
int state;
enum dual_event event;
int new_state;
} dual_fsm_tbl[] = {
{DUAL_STA_PASSIVE, DUAL_EVT_1, 0},
{DUAL_STA_PASSIVE, DUAL_EVT_2, 0},
{DUAL_STA_PASSIVE, DUAL_EVT_3, DUAL_STA_ACTIVE3},
{DUAL_STA_PASSIVE, DUAL_EVT_4, DUAL_STA_ACTIVE1},
{DUAL_STA_ACTIVE0, DUAL_EVT_5, DUAL_STA_ACTIVE2},
{DUAL_STA_ACTIVE0, DUAL_EVT_11, DUAL_STA_ACTIVE1},
{DUAL_STA_ACTIVE0, DUAL_EVT_14, DUAL_STA_PASSIVE},
{DUAL_STA_ACTIVE1, DUAL_EVT_5, DUAL_STA_ACTIVE2},
{DUAL_STA_ACTIVE1, DUAL_EVT_9, DUAL_STA_ACTIVE0},
{DUAL_STA_ACTIVE1, DUAL_EVT_15, DUAL_STA_PASSIVE},
{DUAL_STA_ACTIVE2, DUAL_EVT_12, DUAL_STA_ACTIVE3},
{DUAL_STA_ACTIVE2, DUAL_EVT_16, DUAL_STA_PASSIVE},
{DUAL_STA_ACTIVE3, DUAL_EVT_10, DUAL_STA_ACTIVE2},
{DUAL_STA_ACTIVE3, DUAL_EVT_13, DUAL_STA_PASSIVE},
{DUAL_STA_ACTIVE_ALL, DUAL_EVT_6, 0},
{DUAL_STA_ACTIVE_ALL, DUAL_EVT_7, 0},
{DUAL_STA_ACTIVE_ALL, DUAL_EVT_8, 0},
{-1, 0, 0},
};
static const char * const dual_event_names[] = {
"DUAL_EVT_1",
"DUAL_EVT_2",
"DUAL_EVT_3",
"DUAL_EVT_4",
"DUAL_EVT_5",
"DUAL_EVT_6",
"DUAL_EVT_7",
"DUAL_EVT_8",
"DUAL_EVT_9",
"DUAL_EVT_10",
"DUAL_EVT_11",
"DUAL_EVT_12",
"DUAL_EVT_13",
"DUAL_EVT_14",
"DUAL_EVT_15",
"DUAL_EVT_16"
};
static int
dual_fsm(struct rt_node *rn, enum dual_event event)
{
int old_state;
int new_state = 0;
int i;
old_state = rn->state;
for (i = 0; dual_fsm_tbl[i].state != -1; i++)
if ((dual_fsm_tbl[i].state & old_state) &&
(dual_fsm_tbl[i].event == event)) {
new_state = dual_fsm_tbl[i].new_state;
break;
}
if (dual_fsm_tbl[i].state == -1) {
log_warnx("%s: route %s, event %s not expected in state %s",
__func__, log_prefix(rn), dual_event_names[event],
dual_state_name(old_state));
return (0);
}
if (new_state != 0)
rn->state = new_state;
if (old_state != rn->state) {
log_debug("%s: event %s changing state for prefix %s "
"from %s to %s", __func__, dual_event_names[event],
log_prefix(rn), dual_state_name(old_state),
dual_state_name(rn->state));
if (old_state == DUAL_STA_PASSIVE ||
new_state == DUAL_STA_PASSIVE)
rt_update_fib(rn);
}
return (0);
}
static __inline int
rt_compare(struct rt_node *a, struct rt_node *b)
{
int addrcmp;
addrcmp = eigrp_addrcmp(a->eigrp->af, &a->prefix, &b->prefix);
if (addrcmp != 0)
return (addrcmp);
if (a->prefixlen < b->prefixlen)
return (-1);
if (a->prefixlen > b->prefixlen)
return (1);
return (0);
}
static struct rt_node *
rt_find(struct eigrp *eigrp, struct rinfo *ri)
{
struct rt_node rn;
rn.eigrp = eigrp;
rn.prefix = ri->prefix;
rn.prefixlen = ri->prefixlen;
return (RB_FIND(rt_tree, &eigrp->topology, &rn));
}
static struct rt_node *
rt_new(struct eigrp *eigrp, struct rinfo *ri)
{
struct rt_node *rn;
if ((rn = calloc(1, sizeof(*rn))) == NULL)
fatal("rt_new");
rn->eigrp = eigrp;
rn->prefix = ri->prefix;
rn->prefixlen = ri->prefixlen;
rn->state = DUAL_STA_PASSIVE;
TAILQ_INIT(&rn->routes);
TAILQ_INIT(&rn->rijk);
rt_set_successor(rn, NULL);
if (RB_INSERT(rt_tree, &eigrp->topology, rn) != NULL) {
log_warnx("%s failed for %s", __func__, log_prefix(rn));
free(rn);
return (NULL);
}
log_debug("%s: prefix %s", __func__, log_prefix(rn));
return (rn);
}
void
rt_del(struct rt_node *rn)
{
struct eigrp_route *route;
struct reply_node *reply;
log_debug("%s: prefix %s", __func__, log_prefix(rn));
while ((reply = TAILQ_FIRST(&rn->rijk)) != NULL)
reply_outstanding_remove(reply);
while ((route = TAILQ_FIRST(&rn->routes)) != NULL)
route_del(rn, route);
RB_REMOVE(rt_tree, &rn->eigrp->topology, rn);
free(rn);
}
static struct eigrp_route *
route_find(struct rde_nbr *nbr, struct rt_node *rn)
{
struct eigrp_route *route;
TAILQ_FOREACH(route, &rn->routes, entry)
if (route->nbr == nbr)
return (route);
return (NULL);
}
static struct eigrp_route *
route_new(struct rt_node *rn, struct rde_nbr *nbr, struct rinfo *ri)
{
struct eigrp *eigrp = rn->eigrp;
struct eigrp_route *route, *tmp;
if ((route = calloc(1, sizeof(*route))) == NULL)
fatal("route_new");
route->nbr = nbr;
route->type = ri->type;
if (eigrp_addrisset(eigrp->af, &ri->nexthop))
route->nexthop = ri->nexthop;
else
route->nexthop = nbr->addr;
route_update_metrics(eigrp, route, ri);
TAILQ_FOREACH(tmp, &rn->routes, entry)
if (eigrp_addrcmp(eigrp->af, &tmp->nexthop,
&route->nexthop) > 0)
break;
if (tmp)
TAILQ_INSERT_BEFORE(tmp, route, entry);
else
TAILQ_INSERT_TAIL(&rn->routes, route, entry);
log_debug("%s: prefix %s via %s distance (%u/%u)", __func__,
log_prefix(rn), log_route_origin(eigrp->af, route->nbr),
route->distance, route->rdistance);
return (route);
}
static void
route_del(struct rt_node *rn, struct eigrp_route *route)
{
struct eigrp *eigrp = rn->eigrp;
log_debug("%s: prefix %s via %s", __func__, log_prefix(rn),
log_route_origin(eigrp->af, route->nbr));
if (route->flags & F_EIGRP_ROUTE_INSTALLED)
rde_send_delete_kroute(rn, route);
TAILQ_REMOVE(&rn->routes, route, entry);
free(route);
}
static uint32_t
safe_sum_uint32(uint32_t a, uint32_t b)
{
uint64_t total;
total = (uint64_t) a + (uint64_t) b;
if (total >> 32)
return ((uint32_t )(~0));
return ((uint32_t) total);
}
static uint32_t
safe_mul_uint32(uint32_t a, uint32_t b)
{
uint64_t total;
total = (uint64_t) a * (uint64_t) b;
if (total >> 32)
return ((uint32_t )(~0));
return ((uint32_t) total);
}
uint32_t
eigrp_composite_delay(uint32_t delay)
{
delay = min(delay, (1 << 24) - 1);
return (delay * EIGRP_SCALING_FACTOR);
}
uint32_t
eigrp_real_delay(uint32_t delay)
{
return (delay / EIGRP_SCALING_FACTOR);
}
uint32_t
eigrp_composite_bandwidth(uint32_t bandwidth)
{
bandwidth = 10000000 / bandwidth;
return (EIGRP_SCALING_FACTOR * bandwidth);
}
uint32_t
eigrp_real_bandwidth(uint32_t bandwidth)
{
return ((EIGRP_SCALING_FACTOR * (uint32_t)10000000) / bandwidth);
}
static uint32_t
route_composite_metric(uint8_t *kvalues, uint32_t delay, uint32_t bandwidth,
uint8_t load, uint8_t reliability)
{
uint64_t distance;
uint32_t operand1, operand2, operand3;
double operand4;
operand1 = safe_mul_uint32(kvalues[0] * EIGRP_SCALING_FACTOR,
10000000 / bandwidth);
operand2 = safe_mul_uint32(kvalues[1] * EIGRP_SCALING_FACTOR,
10000000 / bandwidth) / (256 - load);
operand3 = safe_mul_uint32(kvalues[2] * EIGRP_SCALING_FACTOR, delay);
distance = (uint64_t) operand1 + (uint64_t) operand2 +
(uint64_t) operand3;
if (kvalues[4] != 0) {
operand4 = (double) kvalues[4] / (reliability + kvalues[3]);
distance *= operand4;
}
if (distance >> 32)
distance = ((uint32_t )(~0));
return ((uint32_t) distance);
}
static void
route_update_metrics(struct eigrp *eigrp, struct eigrp_route *route,
struct rinfo *ri)
{
struct eigrp_iface *ei = route->nbr->ei;
uint32_t delay, bandwidth;
int mtu;
route->metric = ri->metric;
route->emetric = ri->emetric;
route->flags |= F_EIGRP_ROUTE_M_CHANGED;
delay = eigrp_real_delay(route->metric.delay);
bandwidth = eigrp_real_bandwidth(route->metric.bandwidth);
if (route->nbr->flags & F_RDE_NBR_SELF)
route->rdistance = 0;
else {
route->rdistance = route_composite_metric(eigrp->kvalues,
delay, bandwidth, route->metric.load,
route->metric.reliability);
delay = safe_sum_uint32(delay, ei->delay);
route->metric.delay = eigrp_composite_delay(delay);
bandwidth = min(bandwidth, ei->bandwidth);
route->metric.bandwidth = eigrp_composite_bandwidth(bandwidth);
mtu = min(metric_decode_mtu(route->metric.mtu), ei->iface->mtu);
metric_encode_mtu(route->metric.mtu, mtu);
if (route->metric.hop_count < UINT8_MAX)
route->metric.hop_count++;
}
route->distance = route_composite_metric(eigrp->kvalues, delay,
bandwidth, DEFAULT_LOAD, DEFAULT_RELIABILITY);
}
static void
reply_outstanding_add(struct rt_node *rn, struct rde_nbr *nbr)
{
struct reply_node *reply;
if ((reply = calloc(1, sizeof(*reply))) == NULL)
fatal("reply_outstanding_add");
evtimer_set(&reply->ev_active_timeout, reply_active_timer, reply);
evtimer_set(&reply->ev_sia_timeout, reply_sia_timer, reply);
reply->siaquery_sent = 0;
reply->siareply_recv = 0;
reply->rn = rn;
reply->nbr = nbr;
TAILQ_INSERT_TAIL(&rn->rijk, reply, rn_entry);
TAILQ_INSERT_TAIL(&nbr->rijk, reply, nbr_entry);
if (rn->eigrp->active_timeout > 0) {
reply_active_start_timer(reply);
reply_sia_start_timer(reply);
}
}
static struct reply_node *
reply_outstanding_find(struct rt_node *rn, struct rde_nbr *nbr)
{
struct reply_node *reply;
TAILQ_FOREACH(reply, &rn->rijk, rn_entry)
if (reply->nbr == nbr)
return (reply);
return (NULL);
}
static void
reply_outstanding_remove(struct reply_node *reply)
{
reply_active_stop_timer(reply);
reply_sia_stop_timer(reply);
TAILQ_REMOVE(&reply->rn->rijk, reply, rn_entry);
TAILQ_REMOVE(&reply->nbr->rijk, reply, nbr_entry);
free(reply);
}
static void
reply_active_timer(int fd, short event, void *arg)
{
struct reply_node *reply = arg;
struct rde_nbr *nbr = reply->nbr;
log_debug("%s: neighbor %s is stuck in active", __func__,
log_addr(nbr->eigrp->af, &nbr->addr));
rde_nbr_del(reply->nbr, 1);
}
static void
reply_active_start_timer(struct reply_node *reply)
{
struct eigrp *eigrp = reply->nbr->eigrp;
struct timeval tv;
timerclear(&tv);
tv.tv_sec = eigrp->active_timeout * 60;
if (evtimer_add(&reply->ev_active_timeout, &tv) == -1)
fatal("reply_active_start_timer");
}
static void
reply_active_stop_timer(struct reply_node *reply)
{
if (evtimer_pending(&reply->ev_active_timeout, NULL) &&
evtimer_del(&reply->ev_active_timeout) == -1)
fatal("reply_active_stop_timer");
}
static void
reply_sia_timer(int fd, short event, void *arg)
{
struct reply_node *reply = arg;
struct rde_nbr *nbr = reply->nbr;
struct rt_node *rn = reply->rn;
struct rinfo ri;
log_debug("%s: nbr %s prefix %s", __func__, log_addr(nbr->eigrp->af,
&nbr->addr), log_prefix(rn));
if (reply->siaquery_sent > 0 && reply->siareply_recv == 0) {
log_debug("%s: neighbor %s is stuck in active", __func__,
log_addr(nbr->eigrp->af, &nbr->addr));
rde_nbr_del(nbr, 1);
return;
}
if (reply->siaquery_sent >= 3)
return;
reply->siaquery_sent++;
reply->siareply_recv = 0;
reply_sia_start_timer(reply);
reply_active_start_timer(reply);
rinfo_fill_successor(rn, &ri);
ri.metric.flags |= F_METRIC_ACTIVE;
rde_send_siaquery(nbr, &ri);
}
static void
reply_sia_start_timer(struct reply_node *reply)
{
struct eigrp *eigrp = reply->nbr->eigrp;
struct timeval tv;
timerclear(&tv);
tv.tv_sec = (eigrp->active_timeout * 60) / 2;
if (evtimer_add(&reply->ev_sia_timeout, &tv) == -1)
fatal("reply_sia_start_timer");
}
static void
reply_sia_stop_timer(struct reply_node *reply)
{
if (evtimer_pending(&reply->ev_sia_timeout, NULL) &&
evtimer_del(&reply->ev_sia_timeout) == -1)
fatal("reply_sia_stop_timer");
}
void
rinfo_fill_successor(struct rt_node *rn, struct rinfo *ri)
{
if (rn->successor.nbr == NULL) {
rinfo_fill_infinite(rn, EIGRP_ROUTE_INTERNAL, ri);
return;
}
memset(ri, 0, sizeof(*ri));
ri->af = rn->eigrp->af;
ri->type = rn->successor.type;
ri->prefix = rn->prefix;
ri->prefixlen = rn->prefixlen;
ri->metric = rn->successor.metric;
if (ri->type == EIGRP_ROUTE_EXTERNAL)
ri->emetric = rn->successor.emetric;
}
static void
rinfo_fill_infinite(struct rt_node *rn, enum route_type type, struct rinfo *ri)
{
memset(ri, 0, sizeof(*ri));
ri->af = rn->eigrp->af;
ri->type = type;
ri->prefix = rn->prefix;
ri->prefixlen = rn->prefixlen;
ri->metric.delay = EIGRP_INFINITE_METRIC;
}
static void
rt_update_fib(struct rt_node *rn)
{
struct eigrp *eigrp = rn->eigrp;
uint8_t maximum_paths = eigrp->maximum_paths;
uint8_t variance = eigrp->variance;
int installed = 0;
struct eigrp_route *route;
if (rn->state == DUAL_STA_PASSIVE) {
if (rn->successor.nbr &&
(rn->successor.nbr->flags & F_RDE_NBR_LOCAL))
return;
TAILQ_FOREACH(route, &rn->routes, entry) {
if (route->nbr->flags & F_RDE_NBR_REDIST)
continue;
if (route->rdistance >= rn->successor.fdistance)
goto uninstall;
if (route->distance >
(rn->successor.fdistance * variance))
goto uninstall;
if (installed >= maximum_paths)
goto uninstall;
installed++;
if ((route->flags & F_EIGRP_ROUTE_INSTALLED) &&
!(route->flags & F_EIGRP_ROUTE_M_CHANGED))
continue;
rde_send_change_kroute(rn, route);
continue;
uninstall:
if (route->flags & F_EIGRP_ROUTE_INSTALLED)
rde_send_delete_kroute(rn, route);
}
} else {
TAILQ_FOREACH(route, &rn->routes, entry)
if (route->flags & F_EIGRP_ROUTE_INSTALLED)
rde_send_delete_kroute(rn, route);
}
}
static void
rt_set_successor(struct rt_node *rn, struct eigrp_route *successor)
{
struct eigrp *eigrp = rn->eigrp;
struct eigrp_iface *ei;
struct summary_addr *summary;
if (successor == NULL) {
rn->successor.nbr = NULL;
rn->successor.type = 0;
rn->successor.fdistance = EIGRP_INFINITE_METRIC;
rn->successor.rdistance = EIGRP_INFINITE_METRIC;
memset(&rn->successor.metric, 0,
sizeof(rn->successor.metric));
rn->successor.metric.delay = EIGRP_INFINITE_METRIC;
memset(&rn->successor.emetric, 0,
sizeof(rn->successor.emetric));
} else {
rn->successor.nbr = successor->nbr;
rn->successor.type = successor->type;
rn->successor.fdistance = successor->distance;
rn->successor.rdistance = successor->rdistance;
rn->successor.metric = successor->metric;
rn->successor.emetric = successor->emetric;
}
TAILQ_FOREACH(ei, &eigrp->ei_list, e_entry) {
summary = rde_summary_check(ei, &rn->prefix, rn->prefixlen);
if (summary)
rt_summary_set(eigrp, summary, &rn->successor.metric);
}
}
static struct eigrp_route *
rt_get_successor_fc(struct rt_node *rn)
{
struct eigrp_route *route, *successor = NULL;
uint32_t distance = EIGRP_INFINITE_METRIC;
int external_only = 1;
TAILQ_FOREACH(route, &rn->routes, entry)
if (route->type == EIGRP_ROUTE_INTERNAL) {
if (route->nbr->flags & F_RDE_NBR_LOCAL)
return (route);
external_only = 0;
}
TAILQ_FOREACH(route, &rn->routes, entry) {
if (route->type == EIGRP_ROUTE_EXTERNAL && !external_only)
continue;
if (route->rdistance < rn->successor.fdistance &&
route->distance < distance) {
distance = route->distance;
successor = route;
}
}
return (successor);
}
struct summary_addr *
rde_summary_check(struct eigrp_iface *ei, union eigrpd_addr *prefix,
uint8_t prefixlen)
{
struct summary_addr *summary;
TAILQ_FOREACH(summary, &ei->summary_list, entry) {
if (summary->prefixlen == prefixlen &&
!eigrp_addrcmp(ei->eigrp->af, prefix, &summary->prefix))
return (NULL);
if (summary->prefixlen <= prefixlen &&
!eigrp_prefixcmp(ei->eigrp->af, prefix, &summary->prefix,
summary->prefixlen))
return (summary);
}
return (NULL);
}
static void
rde_send_update(struct eigrp_iface *ei, struct rinfo *ri)
{
if (ri->metric.hop_count >= ei->eigrp->maximum_hops ||
rde_summary_check(ei, &ri->prefix, ri->prefixlen))
ri->metric.delay = EIGRP_INFINITE_METRIC;
rde_imsg_compose_eigrpe(IMSG_SEND_MUPDATE, ei->ifaceid, 0,
ri, sizeof(*ri));
rde_imsg_compose_eigrpe(IMSG_SEND_MUPDATE_END, ei->ifaceid, 0,
NULL, 0);
}
static void
rde_send_update_all(struct rt_node *rn, struct rinfo *ri)
{
struct eigrp *eigrp = rn->eigrp;
struct eigrp_iface *ei;
TAILQ_FOREACH(ei, &eigrp->ei_list, e_entry) {
if (rn->successor.nbr && rn->successor.nbr->ei == ei &&
ei->splithorizon)
continue;
rde_send_update(ei, ri);
}
}
static void
rde_send_query(struct eigrp_iface *ei, struct rinfo *ri, int push)
{
rde_imsg_compose_eigrpe(IMSG_SEND_MQUERY, ei->ifaceid, 0,
ri, sizeof(*ri));
if (push)
rde_imsg_compose_eigrpe(IMSG_SEND_MQUERY_END, ei->ifaceid,
0, NULL, 0);
}
static void
rde_send_siaquery(struct rde_nbr *nbr, struct rinfo *ri)
{
rde_imsg_compose_eigrpe(IMSG_SEND_QUERY, nbr->peerid, 0,
ri, sizeof(*ri));
rde_imsg_compose_eigrpe(IMSG_SEND_SIAQUERY_END, nbr->peerid, 0,
NULL, 0);
}
static void
rde_send_query_all(struct eigrp *eigrp, struct rt_node *rn, int push)
{
struct eigrp_iface *ei;
struct rde_nbr *nbr;
struct rinfo ri;
rinfo_fill_successor(rn, &ri);
ri.metric.flags |= F_METRIC_ACTIVE;
TAILQ_FOREACH(ei, &eigrp->ei_list, e_entry) {
if (rn->successor.nbr && rn->successor.nbr->ei == ei &&
ei->splithorizon)
continue;
rde_send_query(ei, &ri, push);
}
RB_FOREACH(nbr, rde_nbr_head, &rde_nbrs)
if (nbr->ei->eigrp == eigrp && !(nbr->flags & F_RDE_NBR_SELF)) {
if (rn->successor.nbr &&
rn->successor.nbr->ei == nbr->ei &&
nbr->ei->splithorizon)
continue;
reply_outstanding_add(rn, nbr);
}
}
void
rde_flush_queries(void)
{
struct eigrp *eigrp;
struct eigrp_iface *ei;
TAILQ_FOREACH(eigrp, &rdeconf->instances, entry)
TAILQ_FOREACH(ei, &eigrp->ei_list, e_entry)
rde_imsg_compose_eigrpe(IMSG_SEND_MQUERY_END,
ei->ifaceid, 0, NULL, 0);
}
static void
rde_send_reply(struct rde_nbr *nbr, struct rinfo *ri, int siareply)
{
int type;
if (ri->metric.hop_count >= nbr->eigrp->maximum_hops ||
rde_summary_check(nbr->ei, &ri->prefix, ri->prefixlen))
ri->metric.delay = EIGRP_INFINITE_METRIC;
if (!siareply)
type = IMSG_SEND_REPLY_END;
else
type = IMSG_SEND_SIAREPLY_END;
rde_imsg_compose_eigrpe(IMSG_SEND_REPLY, nbr->peerid, 0,
ri, sizeof(*ri));
rde_imsg_compose_eigrpe(type, nbr->peerid, 0, NULL, 0);
}
void
rde_check_update(struct rde_nbr *nbr, struct rinfo *ri)
{
struct eigrp *eigrp = nbr->eigrp;
struct rt_node *rn;
struct eigrp_route *route, *successor;
uint32_t old_fdistance;
struct rinfo sri;
rn = rt_find(eigrp, ri);
if (rn == NULL) {
if (ri->metric.delay == EIGRP_INFINITE_METRIC)
return;
rn = rt_new(eigrp, ri);
route = route_new(rn, nbr, ri);
old_fdistance = EIGRP_INFINITE_METRIC;
} else {
old_fdistance = rn->successor.fdistance;
if (ri->metric.delay == EIGRP_INFINITE_METRIC) {
route = route_find(nbr, rn);
if (route)
route_del(rn, route);
} else {
route = route_find(nbr, rn);
if (route == NULL)
route = route_new(rn, nbr, ri);
else
route_update_metrics(eigrp, route, ri);
}
}
switch (rn->state) {
case DUAL_STA_PASSIVE:
successor = rt_get_successor_fc(rn);
if (successor == NULL) {
rde_send_query_all(eigrp, rn, 1);
dual_fsm(rn, DUAL_EVT_4);
} else {
rt_set_successor(rn, successor);
rt_update_fib(rn);
rinfo_fill_successor(rn, &sri);
if (rn->successor.fdistance != old_fdistance)
rde_send_update_all(rn, &sri);
}
break;
case DUAL_STA_ACTIVE1:
break;
case DUAL_STA_ACTIVE3:
break;
}
if ((rn->state & DUAL_STA_ACTIVE_ALL) && TAILQ_EMPTY(&rn->rijk))
rde_last_reply(rn);
}
void
rde_check_query(struct rde_nbr *nbr, struct rinfo *ri, int siaquery)
{
struct eigrp *eigrp = nbr->eigrp;
struct rt_node *rn;
struct eigrp_route *route, *successor;
uint32_t old_fdistance;
struct rinfo sri;
int reply_sent = 0;
rn = rt_find(eigrp, ri);
if (rn == NULL) {
sri = *ri;
sri.metric.delay = EIGRP_INFINITE_METRIC;
rde_send_reply(nbr, &sri, 0);
if (ri->metric.delay == EIGRP_INFINITE_METRIC)
return;
rn = rt_new(eigrp, ri);
route = route_new(rn, nbr, ri);
rt_set_successor(rn, route);
return;
}
old_fdistance = rn->successor.fdistance;
if (ri->metric.delay == EIGRP_INFINITE_METRIC) {
route = route_find(nbr, rn);
if (route)
route_del(rn, route);
} else {
route = route_find(nbr, rn);
if (route == NULL)
route = route_new(rn, nbr, ri);
else
route_update_metrics(eigrp, route, ri);
}
switch (rn->state) {
case DUAL_STA_PASSIVE:
successor = rt_get_successor_fc(rn);
if (successor == NULL) {
rde_send_query_all(eigrp, rn, 1);
dual_fsm(rn, DUAL_EVT_3);
} else {
rt_set_successor(rn, successor);
rt_update_fib(rn);
rinfo_fill_successor(rn, &sri);
rde_send_reply(nbr, &sri, 0);
reply_sent = 1;
if (rn->successor.fdistance != old_fdistance)
rde_send_update_all(rn, &sri);
}
break;
case DUAL_STA_ACTIVE0:
case DUAL_STA_ACTIVE1:
if (nbr == rn->successor.nbr)
dual_fsm(rn, DUAL_EVT_5);
else {
dual_fsm(rn, DUAL_EVT_6);
rinfo_fill_successor(rn, &sri);
sri.metric.flags |= F_METRIC_ACTIVE;
rde_send_reply(nbr, &sri, 0);
reply_sent = 1;
}
break;
case DUAL_STA_ACTIVE2:
case DUAL_STA_ACTIVE3:
if (nbr == rn->successor.nbr) {
} else {
dual_fsm(rn, DUAL_EVT_6);
rinfo_fill_successor(rn, &sri);
sri.metric.flags |= F_METRIC_ACTIVE;
rde_send_reply(nbr, &sri, 0);
reply_sent = 1;
}
break;
}
if ((rn->state & DUAL_STA_ACTIVE_ALL) && TAILQ_EMPTY(&rn->rijk))
rde_last_reply(rn);
if (siaquery && !reply_sent) {
rinfo_fill_successor(rn, &sri);
sri.metric.flags |= F_METRIC_ACTIVE;
rde_send_reply(nbr, &sri, 1);
}
}
static void
rde_last_reply(struct rt_node *rn)
{
struct eigrp *eigrp = rn->eigrp;
struct eigrp_route *successor;
struct rde_nbr *old_successor;
struct rinfo ri;
old_successor = rn->successor.nbr;
switch (rn->state) {
case DUAL_STA_ACTIVE0:
successor = rt_get_successor_fc(rn);
if (successor == NULL) {
rde_send_query_all(eigrp, rn, 1);
dual_fsm(rn, DUAL_EVT_11);
break;
}
rt_set_successor(rn, successor);
rinfo_fill_successor(rn, &ri);
rde_send_update_all(rn, &ri);
dual_fsm(rn, DUAL_EVT_14);
break;
case DUAL_STA_ACTIVE1:
rn->successor.fdistance = EIGRP_INFINITE_METRIC;
successor = rt_get_successor_fc(rn);
rt_set_successor(rn, successor);
rinfo_fill_successor(rn, &ri);
rde_send_update_all(rn, &ri);
dual_fsm(rn, DUAL_EVT_15);
break;
case DUAL_STA_ACTIVE2:
successor = rt_get_successor_fc(rn);
if (successor == NULL) {
rde_send_query_all(eigrp, rn, 1);
dual_fsm(rn, DUAL_EVT_12);
break;
}
rt_set_successor(rn, successor);
rinfo_fill_successor(rn, &ri);
ri.metric.flags |= F_METRIC_ACTIVE;
if (old_successor)
rde_send_reply(old_successor, &ri, 0);
rde_send_update_all(rn, &ri);
dual_fsm(rn, DUAL_EVT_16);
break;
case DUAL_STA_ACTIVE3:
rn->successor.fdistance = EIGRP_INFINITE_METRIC;
successor = rt_get_successor_fc(rn);
rt_set_successor(rn, successor);
rinfo_fill_successor(rn, &ri);
ri.metric.flags |= F_METRIC_ACTIVE;
if (old_successor)
rde_send_reply(old_successor, &ri, 0);
rde_send_update_all(rn, &ri);
dual_fsm(rn, DUAL_EVT_13);
break;
}
if (rn->state == DUAL_STA_PASSIVE && rn->successor.nbr == NULL)
rt_del(rn);
}
void
rde_check_reply(struct rde_nbr *nbr, struct rinfo *ri, int siareply)
{
struct eigrp *eigrp = nbr->eigrp;
struct rt_node *rn;
struct reply_node *reply;
struct eigrp_route *route;
rn = rt_find(eigrp, ri);
if (rn == NULL)
return;
if (rn->state == DUAL_STA_PASSIVE)
return;
reply = reply_outstanding_find(rn, nbr);
if (reply == NULL)
return;
if (siareply) {
reply->siareply_recv = 1;
reply_active_start_timer(reply);
return;
}
if (ri->metric.delay == EIGRP_INFINITE_METRIC) {
route = route_find(nbr, rn);
if (route)
route_del(rn, route);
} else {
route = route_find(nbr, rn);
if (route == NULL)
route = route_new(rn, nbr, ri);
else
route_update_metrics(eigrp, route, ri);
}
reply_outstanding_remove(reply);
if (TAILQ_EMPTY(&rn->rijk))
rde_last_reply(rn);
}
void
rde_check_link_down_rn(struct rde_nbr *nbr, struct rt_node *rn,
struct eigrp_route *route)
{
struct eigrp *eigrp = nbr->eigrp;
struct reply_node *reply;
struct eigrp_route *successor;
uint32_t old_fdistance;
struct rinfo ri;
enum route_type type;
old_fdistance = rn->successor.fdistance;
type = route->type;
route_del(rn, route);
switch (rn->state) {
case DUAL_STA_PASSIVE:
successor = rt_get_successor_fc(rn);
if (successor == NULL) {
rde_send_query_all(eigrp, rn, 0);
dual_fsm(rn, DUAL_EVT_4);
} else {
rt_set_successor(rn, successor);
rt_update_fib(rn);
rinfo_fill_successor(rn, &ri);
if (rn->successor.fdistance != old_fdistance)
rde_send_update_all(rn, &ri);
}
break;
case DUAL_STA_ACTIVE1:
if (nbr == rn->successor.nbr)
dual_fsm(rn, DUAL_EVT_9);
break;
case DUAL_STA_ACTIVE3:
if (nbr == rn->successor.nbr)
dual_fsm(rn, DUAL_EVT_10);
break;
}
if (rn->state & DUAL_STA_ACTIVE_ALL) {
reply = reply_outstanding_find(rn, nbr);
if (reply) {
rinfo_fill_infinite(rn, type, &ri);
rde_check_reply(nbr, &ri, 0);
}
}
}
void
rde_check_link_down_nbr(struct rde_nbr *nbr)
{
struct eigrp *eigrp = nbr->eigrp;
struct rt_node *rn, *safe;
struct eigrp_route *route;
RB_FOREACH_SAFE(rn, rt_tree, &eigrp->topology, safe) {
route = route_find(nbr, rn);
if (route) {
rde_check_link_down_rn(nbr, rn, route);
if (rn->successor.nbr == nbr)
rn->successor.nbr = NULL;
}
}
}
void
rde_check_link_down(unsigned int ifindex)
{
struct rde_nbr *nbr;
RB_FOREACH(nbr, rde_nbr_head, &rde_nbrs)
if (nbr->ei->iface->ifindex == ifindex)
rde_check_link_down_nbr(nbr);
rde_flush_queries();
}
void
rde_check_link_cost_change(struct rde_nbr *nbr, struct eigrp_iface *ei)
{
}
static __inline int
rde_nbr_compare(struct rde_nbr *a, struct rde_nbr *b)
{
return (a->peerid - b->peerid);
}
struct rde_nbr *
rde_nbr_find(uint32_t peerid)
{
struct rde_nbr n;
n.peerid = peerid;
return (RB_FIND(rde_nbr_head, &rde_nbrs, &n));
}
struct rde_nbr *
rde_nbr_new(uint32_t peerid, struct rde_nbr *new)
{
struct rde_nbr *nbr;
if ((nbr = calloc(1, sizeof(*nbr))) == NULL)
fatal("rde_nbr_new");
nbr->peerid = peerid;
nbr->ifaceid = new->ifaceid;
nbr->addr = new->addr;
nbr->ei = eigrp_if_lookup_id(nbr->ifaceid);
if (nbr->ei)
nbr->eigrp = nbr->ei->eigrp;
TAILQ_INIT(&nbr->rijk);
nbr->flags = new->flags;
if (nbr->peerid != NBR_IDSELF &&
RB_INSERT(rde_nbr_head, &rde_nbrs, nbr) != NULL)
fatalx("rde_nbr_new: RB_INSERT failed");
return (nbr);
}
void
rde_nbr_del(struct rde_nbr *nbr, int peerterm)
{
struct reply_node *reply;
if (peerterm)
rde_imsg_compose_eigrpe(IMSG_NEIGHBOR_DOWN, nbr->peerid,
0, NULL, 0);
while((reply = TAILQ_FIRST(&nbr->rijk)) != NULL)
reply_outstanding_remove(reply);
if (nbr->peerid != NBR_IDSELF)
RB_REMOVE(rde_nbr_head, &rde_nbrs, nbr);
free(nbr);
}