282 lines
7.9 KiB
C
282 lines
7.9 KiB
C
/*
|
|
Copyright (c) 2012, Matthias Schiffer <mschiffer@universe-factory.net>
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright notice,
|
|
this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
#include "ffd.h"
|
|
#include "neigh.h"
|
|
#include "send.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
static void maintain_nexthops(ffd_announce_t *announce) {
|
|
ffd_nexthop_t **cur, **next;
|
|
for (cur = &announce->nexthop_list; *cur; cur = next) {
|
|
ffd_nexthop_t *nexthop = *cur;
|
|
next = &nexthop->next;
|
|
|
|
if (!nexthop->neigh) /* local */
|
|
continue;
|
|
|
|
if (!nexthop->neigh->iface) {
|
|
if (nexthop->metric_seqno.metric != 0xffff) {
|
|
nexthop->metric_seqno.metric = 0xffff;
|
|
nexthop->last_update = now;
|
|
add_interval(&nexthop->last_update, FFD_UPDATE_TIMEOUT(nexthop->interval));
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (timespec_diff(&now, &nexthop->last_update) > FFD_UPDATE_TIMEOUT(nexthop->interval)) {
|
|
if (nexthop->metric_seqno.metric == 0xffff) {
|
|
*cur = *next;
|
|
next = cur;
|
|
|
|
if (announce->selected == nexthop)
|
|
announce->selected = NULL;
|
|
|
|
ffd_neigh_unref(nexthop->neigh);
|
|
|
|
free(nexthop);
|
|
}
|
|
else {
|
|
nexthop->metric_seqno.metric = 0xffff;
|
|
add_interval(&nexthop->last_update, FFD_UPDATE_TIMEOUT(nexthop->interval));
|
|
}
|
|
}
|
|
else if (timespec_diff(&now, &nexthop->last_update) > FFD_UPDATE_REQUEST_TIMEOUT(nexthop->interval) && announce->selected == nexthop) {
|
|
if (!nexthop->requested_update) {
|
|
fprintf(stderr, "info: announce about to expire, requesting update\n");
|
|
ffd_send_announce_request(nexthop->neigh->iface, nexthop->neigh, announce->node, announce->type, announce->key, false);
|
|
nexthop->requested_update = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static ffd_nexthop_t* select_nexthop(const ffd_announce_t *announce) {
|
|
uint16_t ret_metric = 0xffff;
|
|
ffd_nexthop_t *ret = NULL;
|
|
|
|
ffd_nexthop_t *nexthop;
|
|
for (nexthop = announce->nexthop_list; nexthop; nexthop = nexthop->next) {
|
|
if (!nexthop->neigh) /* local */
|
|
return nexthop;
|
|
|
|
if (!ffd_is_feasible(announce, nexthop->metric_seqno))
|
|
continue;
|
|
|
|
uint32_t metric = nexthop->metric_seqno.metric + ffd_neigh_get_cost(nexthop->neigh);
|
|
|
|
if (metric < ret_metric) {
|
|
ret = nexthop;
|
|
ret_metric = metric;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
ffd_metric_seqno_t get_metric(const ffd_announce_t *announce) {
|
|
if (announce->selected) {
|
|
uint32_t metric = announce->selected->metric_seqno.metric + ffd_neigh_get_cost(announce->selected->neigh);
|
|
|
|
if (metric < 0xffff)
|
|
return (ffd_metric_seqno_t){metric, announce->selected->metric_seqno.seqno};
|
|
}
|
|
|
|
return (ffd_metric_seqno_t){0xffff, 0};
|
|
}
|
|
|
|
static inline void seqno_update(ffd_announce_t *announce) {
|
|
ffd_seqno_req_t **cur, **next;
|
|
for (cur = &announce->seqno_req_list; *cur; cur = next) {
|
|
ffd_seqno_req_t *req = *cur;
|
|
next = &req->next;
|
|
|
|
int16_t diff = announce->selected->metric_seqno.seqno - req->seqno;
|
|
|
|
if (diff >= 0) {
|
|
*cur = *next;
|
|
next = cur;
|
|
|
|
fprintf(stderr, "debug: update matches seqno request, forwarding\n");
|
|
ffd_update_enqueue(&announce->node, announce->type, announce->key, req->neigh, true);
|
|
ffd_neigh_unref(req->neigh);
|
|
free(req);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ffd_announce_update(ffd_announce_t *announce) {
|
|
maintain_nexthops(announce);
|
|
|
|
announce->selected = select_nexthop(announce);
|
|
announce->metric = get_metric(announce);
|
|
|
|
if (!announce->selected)
|
|
ffd_send_seqno_request_for(NULL, announce);
|
|
|
|
/* triggered updates */
|
|
int diff = announce->metric.metric - announce->last_metric;
|
|
|
|
if (((announce->last_metric == 0xffff) != (announce->metric.metric == 0xffff))
|
|
|| diff <= -1024 || diff >= 384) {
|
|
fprintf(stderr, "info: announce metric has changed significantly, sending updates\n");
|
|
ffd_update_enqueue(&announce->node, announce->type, announce->key, NULL, announce->metric.metric == 0xffff);
|
|
}
|
|
|
|
if (announce->selected)
|
|
seqno_update(announce);
|
|
}
|
|
|
|
void ffd_announce_update_nexthop(ffd_announce_t *announce, ffd_nexthop_t *nexthop, ffd_metric_seqno_t ms, uint16_t interval) {
|
|
nexthop->metric_seqno = ms;
|
|
nexthop->interval = interval;
|
|
nexthop->requested_update = false;
|
|
|
|
if (ms.metric != 0xffff)
|
|
nexthop->last_update = now;
|
|
|
|
ffd_announce_update(announce);
|
|
}
|
|
|
|
ffd_announce_t* ffd_announce_new(void) {
|
|
ffd_announce_t *a = calloc(1, sizeof(ffd_announce_t));
|
|
|
|
a->metric.metric = a->feasibility_distance.metric = a->last_metric = 0xffff;
|
|
|
|
a->next = announce_list;
|
|
announce_list = a;
|
|
|
|
return a;
|
|
}
|
|
|
|
ffd_announce_t* ffd_announce_find(const ffd_node_id_t *node, uint16_t type, uint16_t key) {
|
|
ffd_announce_t *announce;
|
|
for (announce = announce_list; announce; announce = announce->next) {
|
|
if (ffd_are_node_ids_equal(&announce->node, node)
|
|
&& announce->type == type
|
|
&& announce->key == key)
|
|
return announce;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
ffd_announce_t* ffd_announce_get(const ffd_node_id_t *node, uint16_t type, uint16_t key) {
|
|
ffd_announce_t *announce = ffd_announce_find(node, type, key);
|
|
|
|
if (!announce) {
|
|
announce = ffd_announce_new();
|
|
announce->node = *node;
|
|
announce->type = type;
|
|
announce->key = key;
|
|
}
|
|
|
|
return announce;
|
|
}
|
|
|
|
ffd_nexthop_t* ffd_announce_nexthop_find(const ffd_announce_t *announce, ffd_neigh_t *neigh) {
|
|
ffd_nexthop_t *nexthop;
|
|
for (nexthop = announce->nexthop_list; nexthop; nexthop = nexthop->next) {
|
|
if (nexthop->neigh == neigh)
|
|
return nexthop;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
ffd_nexthop_t* ffd_announce_nexthop_new(ffd_announce_t *announce, ffd_neigh_t *neigh) {
|
|
ffd_nexthop_t *nexthop = calloc(1, sizeof(ffd_nexthop_t));
|
|
nexthop->neigh = neigh;
|
|
|
|
nexthop->next = announce->nexthop_list;
|
|
announce->nexthop_list = nexthop;
|
|
|
|
ffd_neigh_ref(neigh);
|
|
|
|
return nexthop;
|
|
}
|
|
|
|
static inline bool find_seqno_request(ffd_seqno_req_t **reqp, ffd_announce_t *announce, ffd_neigh_t *neigh, uint16_t seqno) {
|
|
ffd_seqno_req_t *req;
|
|
bool ret = true;
|
|
|
|
*reqp = NULL;
|
|
|
|
for (req = announce->seqno_req_list; req; req = req->next) {
|
|
if (req->neigh == neigh)
|
|
*reqp = req;
|
|
|
|
int16_t diff = req->seqno - seqno;
|
|
if (timespec_diff(&now, &req->last_req) < FFD_SEQNO_REQUEST_TIMEOUT && diff >= 0)
|
|
ret = false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool ffd_announce_seqno_request(ffd_announce_t *announce, ffd_neigh_t *neigh, uint16_t seqno) {
|
|
ffd_seqno_req_t *req;
|
|
bool ret = find_seqno_request(&req, announce, neigh, seqno);
|
|
|
|
if (!req) {
|
|
req = calloc(1, sizeof(ffd_seqno_req_t));
|
|
req->neigh = neigh;
|
|
req->seqno = seqno;
|
|
req->last_req = now;
|
|
|
|
ffd_neigh_ref(neigh);
|
|
|
|
req->next = announce->seqno_req_list;
|
|
announce->seqno_req_list = req;
|
|
}
|
|
|
|
int16_t diff = seqno - req->seqno;
|
|
if (diff > 0) {
|
|
req->seqno = seqno;
|
|
req->last_req = now;
|
|
}
|
|
|
|
if (ret) {
|
|
req->last_req = now;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ffd_announce_free(ffd_announce_t *announce) {
|
|
ffd_seqno_req_t *req, *req_next;
|
|
for (req = announce->seqno_req_list; req; req = req_next) {
|
|
req_next = req->next;
|
|
ffd_neigh_unref(req->neigh);
|
|
free(req);
|
|
}
|
|
|
|
free(announce);
|
|
}
|