diff --git a/ffd/CMakeLists.txt b/ffd/CMakeLists.txt index 1571580..2cc8cd4 100644 --- a/ffd/CMakeLists.txt +++ b/ffd/CMakeLists.txt @@ -1,12 +1,15 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${FFD_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}) add_executable(ffd + ack.c announce.c ffd.c neigh.c netif.c + queue.c send.c tlv.c + update.c util.c ) target_link_libraries(ffd rt) diff --git a/ffd/ack.c b/ffd/ack.c new file mode 100644 index 0000000..cd21d0f --- /dev/null +++ b/ffd/ack.c @@ -0,0 +1,91 @@ +/* + Copyright (c) 2012, Matthias Schiffer + 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 "queue.h" + + +static uint8_t acks[1 << 13] = {}; +static ffd_queue_t *ack_requests = NULL; +static uint16_t nonce = 0; + + +#define SET_ACK(n) do {acks[n >> 3] |= 1 << (n & 7);} while(0) +#define UNSET_ACK(n) do {acks[n >> 3] &= ~(1 << (n & 7));} while(0) +#define GET_ACK(n) ((acks[n >> 3] & (1 << (n & 7))) != 0) + + +typedef struct _ack_arg_t { + void (*cb)(uint16_t nonce, void *arg); + void (*free)(uint16_t nonce, void *arg); + void *arg; + + uint16_t nonce; + unsigned interval; + unsigned retries; +} ack_arg_t; + + +void ffd_ack_handle(uint16_t n) { + SET_ACK(n); +} + +static void ack_resend(const struct timespec *timeout, void *argp) { + ack_arg_t *arg = argp; + + if (GET_ACK(arg->nonce) || !(--arg->retries)) { + arg->free(arg->nonce, arg->arg); + free(arg); + return; + } + + arg->cb(arg->nonce, arg->arg); + + ffd_queue_put_delayed(&ack_requests, ack_resend, timeout, arg->interval, arg); +} + +void ffd_ack_request(void (*cb)(uint16_t nonce, void *arg), void (*free_cb)(uint16_t nonce, void *arg), unsigned interval, unsigned retries, void *arg) { + UNSET_ACK(nonce); + cb(nonce, arg); + + ack_arg_t *ack_arg = malloc(sizeof(ack_arg_t)); + ack_arg->cb = cb; + ack_arg->free = free_cb; + ack_arg->arg = arg; + ack_arg->nonce = nonce++; + ack_arg->interval = interval; + ack_arg->retries = retries; + + ffd_queue_put_delayed(&ack_requests, ack_resend, &now, interval, ack_arg); +} + +int ffd_ack_timeout(void) { + return ffd_queue_timeout(&ack_requests); +} + +void ffd_ack_run(void) { + ffd_queue_run(&ack_requests); +} diff --git a/ffd/announce.c b/ffd/announce.c index 26ced28..f68bb48 100644 --- a/ffd/announce.c +++ b/ffd/announce.c @@ -26,6 +26,7 @@ #include "ffd.h" #include "neigh.h" +#include "send.h" #include @@ -110,6 +111,26 @@ ffd_metric_seqno_t get_metric(const ffd_announce_t *announce) { 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); @@ -125,8 +146,11 @@ void ffd_announce_update(ffd_announce_t *announce) { 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_send_update(NULL, NULL, announce, false); + 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) { @@ -175,3 +199,84 @@ ffd_announce_t* ffd_announce_get(const ffd_node_id_t *node, uint16_t type, uint1 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); +} diff --git a/ffd/ffd.c b/ffd/ffd.c index 5e967f3..0504085 100644 --- a/ffd/ffd.c +++ b/ffd/ffd.c @@ -28,6 +28,8 @@ #include "neigh.h" #include "netif.h" #include "packet.h" +#include "queue.h" +#include "send.h" #include "tlv.h" #include "tlv_types.h" @@ -51,10 +53,10 @@ static char *mesh = "bat0"; int sockfd; struct timespec now; +static ffd_queue_t *tasks = NULL; + ffd_node_id_t self; - ffd_iface_t *iface_list = NULL; - ffd_announce_t *announce_list = NULL; @@ -201,6 +203,15 @@ static void handle_tlv_ack_req(const ffd_tlv_ack_req_t *tlv_req, size_t len, han ffd_send_ack(neigh, ntohs(tlv_req->nonce)); } +static void handle_tlv_ack(const ffd_tlv_ack_t *tlv_ack, size_t len, handle_tlv_arg_t *arg) { + if (len < sizeof(ffd_tlv_ack_t)) { + fprintf(stderr, "warn: received short acknowledement TLV.\n"); + return; + } + + ffd_ack_handle(ntohs(tlv_ack->nonce)); +} + static void handle_tlv_hello(const ffd_tlv_hello_t *tlv_hello, size_t len, handle_tlv_arg_t *arg) { if (len < sizeof(ffd_tlv_hello_t)) { fprintf(stderr, "warn: received short hello TLV.\n"); @@ -284,28 +295,6 @@ static void handle_tlv_node_id(const ffd_tlv_node_id_t *tlv_node_id, size_t len, arg->node_id = tlv_node_id->id; } -static ffd_nexthop_t* find_nexthop(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; -} - -static ffd_nexthop_t* new_nexthop(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 void handle_tlv_update(const ffd_tlv_update_t *tlv_update, size_t len, handle_tlv_arg_t *arg) { if (len < sizeof(ffd_tlv_update_t)) { fprintf(stderr, "warn: received short update TLV.\n"); @@ -329,11 +318,11 @@ static void handle_tlv_update(const ffd_tlv_update_t *tlv_update, size_t len, ha bool feasible = ffd_is_feasible(announce, ms); ffd_neigh_t *neigh = get_tlv_neigh(arg); - ffd_nexthop_t *nexthop = find_nexthop(announce, neigh); + ffd_nexthop_t *nexthop = ffd_announce_nexthop_find(announce, neigh); if (!nexthop) { if (feasible && tlv_update->metric != 0xffff /* no need to ntohs */) - nexthop = new_nexthop(announce, neigh); + nexthop = ffd_announce_nexthop_new(announce, neigh); } else { if (!feasible && nexthop == announce->selected) { @@ -397,8 +386,6 @@ static void handle_tlv_seqno_req(const ffd_tlv_seqno_req_t *tlv_req, size_t len, return; } - fprintf(stderr, "debug: received seqno request\n"); - ffd_announce_t *announce = ffd_announce_find(&tlv_req->node, ntohs(tlv_req->type), ntohs(tlv_req->key)); if (!announce) { @@ -416,21 +403,28 @@ static void handle_tlv_seqno_req(const ffd_tlv_seqno_req_t *tlv_req, size_t len, if (announce->selected->neigh == neigh) return; - if ((int16_t)(ntohs(tlv_req->seqno)-announce->selected->metric_seqno.seqno) <= 0) { + uint16_t seqno = ntohs(tlv_req->seqno); + + if ((int16_t)(seqno-announce->selected->metric_seqno.seqno) <= 0) { fprintf(stderr, "debug: received seqno request, seqno already ok\n"); - ffd_send_update(NULL, neigh, announce, false); + ffd_update_enqueue(&announce->node, announce->type, announce->key, neigh, true); return; } if (!announce->selected->neigh) { - fprintf(stderr, "debug: incrementing seqno\n"); + fprintf(stderr, "debug: received seqno request, incrementing seqno\n"); announce->selected->metric_seqno.seqno++; - ffd_send_update(NULL, neigh, announce, false); + ffd_update_enqueue(&announce->node, announce->type, announce->key, neigh, true); return; } - fprintf(stderr, "debug: received seqno request, forwarding\n"); - ffd_send_seqno_request(announce->selected->neigh, announce, ntohs(tlv_req->seqno)); + if (ffd_announce_seqno_request(announce, neigh, seqno)) { + fprintf(stderr, "debug: received seqno request, forwarding\n"); + ffd_send_seqno_request(announce->selected->neigh, announce, seqno); + } + else { + fprintf(stderr, "debug: received seqno request, not forwarding again\n"); + } } static void handle_tlv(ffd_tlv_type_t type, const void *data, size_t len, void *arg) { @@ -440,7 +434,7 @@ static void handle_tlv(ffd_tlv_type_t type, const void *data, size_t len, void * return; case TLV_ACK: - /* we don't send ack reqs */ + handle_tlv_ack(data, len, arg); return; case TLV_HELLO: @@ -506,6 +500,8 @@ static void receive_packet(void) { } static void send_updates(void) { + fprintf(stderr, "debug: sending periodic updates.\n"); + ffd_iface_t *iface; for (iface = iface_list; iface; iface = iface->next) { ffd_send_update(iface, NULL, NULL, false); @@ -547,7 +543,7 @@ static void maintenance(void) { if (!announce->nexthop_list) { *cur = *next; next = cur; - free(announce); + ffd_announce_free(announce); continue; } @@ -574,6 +570,46 @@ static void maintenance(void) { } } + +typedef struct _periodic_task_info { + void (*handle)(void); + unsigned delay; + unsigned interval; +} periodic_task_info; + + +static const periodic_task_info periodic_tasks [] = { + {maintenance, 0, FFD_MAINTENANCE_INTERVAL}, + {ffd_send_hellos, FFD_HELLO_INTERVAL, FFD_HELLO_INTERVAL}, + {send_updates, 1, FFD_UPDATE_INTERVAL}, + {NULL} +}; + + +static void handle_periodic_task(const struct timespec *timeout, void *arg) { + const periodic_task_info *info = arg; + + info->handle(); + ffd_queue_put_delayed(&tasks, handle_periodic_task, timeout, info->interval, arg); +} + +static void register_periodic_tasks(void) { + const periodic_task_info *info; + for (info = periodic_tasks; info->handle; info++) + ffd_queue_put_delayed(&tasks, handle_periodic_task, &now, info->delay, (void*)info); +} + + +static inline int timeout_min(int a, int b) { + if (a < 0) + return b; + else if (b < 0) + return a; + else + return min(a, b); +} + + int main() { if (!check_config()) return 1; @@ -585,44 +621,23 @@ int main() { return 1; update_time(); - - struct timespec next_hello = now; - struct timespec next_update = now; - struct timespec next_maintenance = now; + register_periodic_tasks(); while (true) { - int maintenance_timeout = timespec_diff(&next_maintenance, &now); - if (maintenance_timeout <= 0) { - maintenance(); - - add_interval(&next_maintenance, FFD_MAINTENANCE_INTERVAL); - } - - int hello_timeout = timespec_diff(&next_hello, &now); - if (hello_timeout <= 0) { - ffd_send_hellos(); - - add_interval(&next_hello, FFD_HELLO_INTERVAL); - } - - int update_timeout = timespec_diff(&next_update, &now); - if (update_timeout <= 0) { - fprintf(stderr, "Sending periodic update.\n"); - send_updates(); - - add_interval(&next_update, FFD_UPDATE_INTERVAL); - } - - int timeout = min(min(hello_timeout, update_timeout), maintenance_timeout); - if (timeout <= 0) - continue; + ffd_queue_run(&tasks); + ffd_update_run(); + ffd_ack_run(); struct pollfd fds[1]; + int timeout = 10*timeout_min(timeout_min(ffd_queue_timeout(&tasks), ffd_update_timeout()), ffd_ack_timeout()); + if (timeout < 0) + timeout = -1; + fds[0].fd = sockfd; fds[0].events = POLLIN; - poll(fds, 1, 10*timeout); + poll(fds, 1, timeout); update_time(); diff --git a/ffd/ffd.h b/ffd/ffd.h index e7c48d7..2999be4 100644 --- a/ffd/ffd.h +++ b/ffd/ffd.h @@ -47,8 +47,13 @@ #define FFD_UPDATE_REQUEST_TIMEOUT(interval) ((interval)*13/4) /* 3.25 intervals */ +#define FFD_SEQNO_REQUEST_TIMEOUT 50 + #define FFD_MAINTENANCE_INTERVAL FFD_HELLO_INTERVAL +#define FFD_DELAY (FFD_HELLO_INTERVAL/2) +#define FFD_URGENT_DELAY 20 +#define FFD_ACK_INTERVAL FFD_URGENT_DELAY #define FFD_UPDATE_WITH_DATA 0x01 @@ -73,6 +78,14 @@ typedef struct _ffd_nexthop_t { bool requested_update; } ffd_nexthop_t; +typedef struct _ffd_seqno_req_t { + struct _ffd_seqno_req_t *next; + + struct timespec last_req; + struct _ffd_neigh_t *neigh; + uint16_t seqno; +} ffd_seqno_req_t; + typedef struct _ffd_announce_t { struct _ffd_announce_t *next; @@ -84,6 +97,8 @@ typedef struct _ffd_announce_t { uint16_t last_metric; ffd_metric_seqno_t feasibility_distance; + ffd_seqno_req_t *seqno_req_list; + ffd_nexthop_t *selected; ffd_nexthop_t *nexthop_list; @@ -133,7 +148,6 @@ extern ffd_iface_t *iface_list; extern ffd_announce_t *announce_list; extern int sockfd; -extern struct timespec now; #define FFD_NODE_ID_UNSPEC ((ffd_node_id_t){}) @@ -189,19 +203,18 @@ void ffd_announce_update_nexthop(ffd_announce_t *announce, ffd_nexthop_t *nextho ffd_announce_t* ffd_announce_new(void); ffd_announce_t* ffd_announce_find(const ffd_node_id_t *node, uint16_t type, uint16_t key); ffd_announce_t* ffd_announce_get(const ffd_node_id_t *node, uint16_t type, uint16_t key); +ffd_nexthop_t* ffd_announce_nexthop_find(const ffd_announce_t *announce, ffd_neigh_t *neigh); +ffd_nexthop_t* ffd_announce_nexthop_new(ffd_announce_t *announce, ffd_neigh_t *neigh); +bool ffd_announce_seqno_request(ffd_announce_t *announce, ffd_neigh_t *neigh, uint16_t seqno); +void ffd_announce_free(ffd_announce_t *announce); -void ffd_send_ack(ffd_neigh_t *neigh, uint16_t nonce); -void ffd_send_hellos(void); -void ffd_send_update(ffd_iface_t *iface, ffd_neigh_t *neigh, ffd_announce_t *announce, bool with_data); -void ffd_send_retract(ffd_neigh_t *neigh, ffd_node_id_t node, uint16_t type, uint16_t key); -void ffd_send_announce_request(ffd_iface_t *iface, ffd_neigh_t *neigh, ffd_node_id_t node, uint16_t type, uint16_t key, bool with_data); -void ffd_send_seqno_request(ffd_neigh_t *neigh, ffd_announce_t *announce, uint16_t seqno); +void ffd_ack_handle(uint16_t n); +void ffd_ack_request(void (*cb)(uint16_t nonce, void *arg), void (*free_cb)(uint16_t nonce, void *arg), unsigned interval, unsigned retries, void *arg); +int ffd_ack_timeout(void); +void ffd_ack_run(void); -static inline void ffd_send_seqno_request_for(ffd_neigh_t *neigh, ffd_announce_t *announce) { - if (FFD_IS_INFINITY(announce->feasibility_distance)) - return; - - ffd_send_seqno_request(neigh, announce, announce->feasibility_distance.seqno+1); -} +void ffd_update_enqueue(const ffd_node_id_t *node, uint16_t type, uint16_t key, ffd_neigh_t *neigh, bool urgent); +int ffd_update_timeout(void); +void ffd_update_run(void); #endif /* _FFD_FFD_H_ */ diff --git a/ffd/neigh.c b/ffd/neigh.c index 1b0d41d..e8e7d0f 100644 --- a/ffd/neigh.c +++ b/ffd/neigh.c @@ -25,6 +25,7 @@ #include "neigh.h" +#include "send.h" ffd_neigh_t* ffd_neigh_get(ffd_iface_t *iface, const eth_addr_t *addr) { @@ -55,6 +56,11 @@ void ffd_neigh_unref_list(ffd_neigh_t *neigh) { } } +void ffd_neigh_unref(ffd_neigh_t *neigh) { + if (!(--neigh->ref) && !neigh->iface) + free(neigh); +} + uint16_t ffd_neigh_get_rxcost(const ffd_neigh_t *neigh) { if (!neigh->hello_log || !neigh->hello_interval || !neigh->iface) return 0xffff; diff --git a/ffd/neigh.h b/ffd/neigh.h index aa0d1f9..3e84a8e 100644 --- a/ffd/neigh.h +++ b/ffd/neigh.h @@ -44,10 +44,6 @@ static inline void ffd_neigh_ref(ffd_neigh_t *neigh) { neigh->ref++; } -static inline void ffd_neigh_unref(ffd_neigh_t *neigh) { - if (!(--neigh->ref) && !neigh->iface) - free(neigh); -} ffd_neigh_t* ffd_neigh_get(ffd_iface_t *iface, const eth_addr_t *addr); void ffd_neigh_unref(ffd_neigh_t *neigh); diff --git a/ffd/queue.c b/ffd/queue.c new file mode 100644 index 0000000..23f96bc --- /dev/null +++ b/ffd/queue.c @@ -0,0 +1,42 @@ +/* + Copyright (c) 2012, Matthias Schiffer + 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 "queue.h" + + +void ffd_queue_put(ffd_queue_t **queue, ffd_queue_cb cb, const struct timespec *timeout, void *arg) { + while (*queue && timespec_after(timeout, &(*queue)->timeout)) + queue = &(*queue)->next; + + ffd_queue_t *entry = malloc(sizeof(ffd_queue_t)); + + entry->cb = cb; + entry->timeout = *timeout; + entry->arg = arg; + + entry->next = *queue; + *queue = entry; +} diff --git a/ffd/queue.h b/ffd/queue.h new file mode 100644 index 0000000..f9b8008 --- /dev/null +++ b/ffd/queue.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2012, Matthias Schiffer + 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. +*/ + + +#ifndef _FFD_QUEUE_H_ +#define _FFD_QUEUE_H_ + +#include "util.h" + + +typedef void (*ffd_queue_cb)(const struct timespec *timeout, void *arg); + +struct _ffd_queue_t { + ffd_queue_t *next; + ffd_queue_cb cb; + struct timespec timeout; + void *arg; +}; + + +void ffd_queue_put(ffd_queue_t **queue, ffd_queue_cb cb, const struct timespec *timeout, void *arg); + + +static inline void ffd_queue_put_delayed(ffd_queue_t **queue, ffd_queue_cb cb, const struct timespec *timeout, int delay, void *arg) { + struct timespec timeout_delayed = *timeout; + add_interval(&timeout_delayed, delay); + ffd_queue_put(queue, cb, &timeout_delayed, arg); +} + +static inline void ffd_queue_drop(ffd_queue_t **queue) { + ffd_queue_t *entry = *queue; + *queue = entry->next; + free(entry); +} + +static inline void ffd_queue_run(ffd_queue_t **queue) { + while (*queue && timespec_after(&now, &(*queue)->timeout)) { + ffd_queue_t *entry = *queue; + *queue = (*queue)->next; + + entry->cb(&entry->timeout, entry->arg); + free(entry); + } +} + +static inline int ffd_queue_timeout(ffd_queue_t *const *queue) { + if (!*queue) + return -1; + + return max(timespec_diff(&(*queue)->timeout, &now), 0); +} + +#endif /* _FFD_QUEUE_H_ */ diff --git a/ffd/send.c b/ffd/send.c index ebda0de..2cd57bf 100644 --- a/ffd/send.c +++ b/ffd/send.c @@ -27,6 +27,7 @@ #include "ffd.h" #include "neigh.h" #include "packet.h" +#include "send.h" #include "tlv.h" #include "tlv_types.h" @@ -40,6 +41,27 @@ #include + +typedef struct _announce_info_t { + ffd_node_id_t node; + uint16_t type; + uint16_t key; +} announce_info_t; + +struct _ffd_update_t { + ffd_iface_t *iface; + ffd_neigh_t *neigh; + + ffd_packet_t *packet; + size_t max_len; + + ffd_node_id_t node; + + unsigned n_updates; + announce_info_t announces[]; +}; + + static bool send_eth(const eth_addr_t *addr, unsigned ifindex, const void *buf, size_t len) { static const uint8_t zeros[46] = {0}; @@ -166,6 +188,18 @@ void ffd_send_hellos(void) { } } +static bool add_ack_request(ffd_packet_t *packet, uint16_t nonce) { + ffd_tlv_ack_req_t *tlv = ffd_tlv_add(packet, FFD_PACKET_MAX, TLV_ACK_REQ, sizeof(ffd_tlv_ack_req_t)); + if (!tlv) + return false; + + tlv->reserved = 0; + tlv->nonce = htons(nonce); + tlv->interval = htons(FFD_ACK_INTERVAL); + + return true; +} + static bool add_node_id(ffd_packet_t *packet, size_t max_len, ffd_node_id_t node_id) { ffd_tlv_node_id_t *tlv = ffd_tlv_add(packet, FFD_PACKET_MAX, TLV_NODE_ID, sizeof(ffd_tlv_node_id_t)); if (!tlv) @@ -223,6 +257,34 @@ static bool add_update(ffd_packet_t *packet, size_t max_len, ffd_node_id_t *node return true; } +static bool add_retract(ffd_packet_t *packet, size_t max_len, ffd_node_id_t *node_id, ffd_node_id_t node, uint16_t type, uint16_t key) { + uint16_t len = packet->len; + + if (!node_id || !ffd_are_node_ids_equal(node_id, &node)) { + if (!add_node_id(packet, max_len, node)) + return false; + + if (node_id) + *node_id = node; + } + + ffd_tlv_update_t *update = ffd_tlv_add(packet, FFD_PACKET_MAX, TLV_UPDATE, sizeof(ffd_tlv_update_t)); + if (!update) { + packet->len = len; + return false; + } + + update->flags = 0; + update->reserved = 0; + update->interval = htons(FFD_UPDATE_INTERVAL); + update->seqno = 0; + update->metric = 0xffff; /* no need to htons */ + update->type = htons(type); + update->key = htons(key); + + return true; +} + void ffd_send_update(ffd_iface_t *iface, ffd_neigh_t *neigh, ffd_announce_t *announce, bool with_data) { ffd_packet_t *packet = alloca(sizeof(ffd_packet_t)+FFD_PACKET_MAX); @@ -264,21 +326,83 @@ void ffd_send_retract(ffd_neigh_t *neigh, ffd_node_id_t node, uint16_t type, uin if (!add_node_id(packet, FFD_PACKET_MAX, node)) return; - ffd_tlv_update_t *req = ffd_tlv_add(packet, FFD_PACKET_MAX, TLV_UPDATE, sizeof(ffd_tlv_update_t)); - if (!req) + ffd_tlv_update_t *update = ffd_tlv_add(packet, FFD_PACKET_MAX, TLV_UPDATE, sizeof(ffd_tlv_update_t)); + if (!update) return; - req->flags = 0; - req->reserved = 0; - req->interval = htons(FFD_UPDATE_INTERVAL); - req->seqno = 0; - req->metric = 0xffff; /* no need to htons */ - req->type = htons(type); - req->key = htons(key); + update->flags = 0; + update->reserved = 0; + update->interval = htons(FFD_UPDATE_INTERVAL); + update->seqno = 0; + update->metric = 0xffff; /* no need to htons */ + update->type = htons(type); + update->key = htons(key); send_neigh(neigh, packet); } +ffd_update_t* ffd_send_update_new(ffd_iface_t *iface, ffd_neigh_t *neigh, const uint16_t *nonce) { + unsigned max_updates = (FFD_PACKET_MAX+sizeof(ffd_tlv_update_t)+1)/(sizeof(ffd_tlv_update_t)+2); + ffd_update_t *update = calloc(1, sizeof(ffd_update_t) + max_updates*sizeof(announce_info_t)); + update->iface = iface; + update->neigh = neigh; + + update->max_len = FFD_PACKET_MAX; + update->packet = malloc(sizeof(ffd_packet_t)+FFD_PACKET_MAX); + update->packet->version_magic = htons(FFD_VERSION_MAGIC); + update->packet->len = 0; + + if (nonce) + add_ack_request(update->packet, *nonce); + + return update; +} + +static inline bool is_update_duplicate(ffd_update_t *update, ffd_node_id_t node, uint16_t type, uint16_t key) { + unsigned i; + for (i = 0; i < update->n_updates; i++) { + if (ffd_are_node_ids_equal(&update->announces[i].node, &node) + && update->announces[i].type == type && update->announces[i].key == key) + return true; + } + + return false; +} + +bool ffd_send_update_add(ffd_update_t *update, ffd_announce_t *announce) { + if (is_update_duplicate(update, announce->node, announce->type, announce->key)) + return false; + + if (!add_update(update->packet, update->max_len, &update->node, announce, false, update->neigh)) + return false; + + update->announces[update->n_updates++] = (announce_info_t){announce->node, announce->type, announce->key}; + + return true; +} + +bool ffd_send_update_retract(ffd_update_t *update, ffd_node_id_t node, uint16_t type, uint16_t key) { + if (is_update_duplicate(update, node, type, key)) + return false; + + if (!add_retract(update->packet, update->max_len, &update->node, node, type, key)) + return false; + + update->announces[update->n_updates++] = (announce_info_t){node, type, key}; + + return true; +} + +void ffd_send_update_finish(ffd_update_t *update) { + fprintf(stderr, "debug: sending %u aggregated update(s).\n", update->n_updates); + + if (update->packet->len) + send_any(update->iface, update->neigh, update->packet); + + free(update->packet); + free(update); +} + void ffd_send_announce_request(ffd_iface_t *iface, ffd_neigh_t *neigh, ffd_node_id_t node, uint16_t type, uint16_t key, bool with_data) { ffd_packet_t *packet = alloca(sizeof(ffd_packet_t)+FFD_PACKET_MAX); diff --git a/ffd/send.h b/ffd/send.h new file mode 100644 index 0000000..50a61e2 --- /dev/null +++ b/ffd/send.h @@ -0,0 +1,56 @@ +/* + Copyright (c) 2012, Matthias Schiffer + 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. +*/ + + +#ifndef _FFD_SEND_H_ +#define _FFD_SEND_H_ + +#include "ffd.h" + + +typedef struct _ffd_update_t ffd_update_t; + + +void ffd_send_ack(ffd_neigh_t *neigh, uint16_t nonce); +void ffd_send_hellos(void); +void ffd_send_update(ffd_iface_t *iface, ffd_neigh_t *neigh, ffd_announce_t *announce, bool with_data); +void ffd_send_retract(ffd_neigh_t *neigh, ffd_node_id_t node, uint16_t type, uint16_t key); +void ffd_send_announce_request(ffd_iface_t *iface, ffd_neigh_t *neigh, ffd_node_id_t node, uint16_t type, uint16_t key, bool with_data); +void ffd_send_seqno_request(ffd_neigh_t *neigh, ffd_announce_t *announce, uint16_t seqno); + +ffd_update_t* ffd_send_update_new(ffd_iface_t *iface, ffd_neigh_t *neigh, const uint16_t *nonce); +bool ffd_send_update_add(ffd_update_t *update, ffd_announce_t *announce); +bool ffd_send_update_retract(ffd_update_t *update, ffd_node_id_t node, uint16_t type, uint16_t key); +void ffd_send_update_finish(ffd_update_t *update); + + +static inline void ffd_send_seqno_request_for(ffd_neigh_t *neigh, ffd_announce_t *announce) { + if (FFD_IS_INFINITY(announce->feasibility_distance)) + return; + + ffd_send_seqno_request(neigh, announce, announce->feasibility_distance.seqno+1); +} + +#endif /* _FFD_SEND_H_ */ diff --git a/ffd/types.h b/ffd/types.h index f0c6c39..b20d691 100644 --- a/ffd/types.h +++ b/ffd/types.h @@ -41,6 +41,6 @@ typedef struct __attribute__((packed)) _eth_addr_t { typedef struct _ffd_packet_t ffd_packet_t; - +typedef struct _ffd_queue_t ffd_queue_t; #endif /* _FFD_TYPES_H_ */ diff --git a/ffd/update.c b/ffd/update.c new file mode 100644 index 0000000..7ae760a --- /dev/null +++ b/ffd/update.c @@ -0,0 +1,158 @@ +/* + Copyright (c) 2012, Matthias Schiffer + 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 "queue.h" +#include "send.h" + +#include + + +static ffd_queue_t *pending_updates = NULL; + + +typedef struct _update_arg_t { + ffd_node_id_t node; + uint16_t type; + uint16_t key; + + ffd_neigh_t *neigh; + unsigned ref; +} update_arg_t; + + +static inline update_arg_t* update_new(const ffd_node_id_t *node, uint16_t type, uint16_t key, ffd_neigh_t *neigh) { + update_arg_t *arg = malloc(sizeof(update_arg_t)); + + arg->node = *node; + arg->type = type; + arg->key = key; + + if (neigh) + ffd_neigh_ref(neigh); + arg->neigh = neigh; + + arg->ref = 0; + + return arg; +} + +static inline update_arg_t* update_ref(update_arg_t *arg) { + arg->ref++; + return arg; +} + +static inline void update_unref(update_arg_t *arg) { + if (!--arg->ref) { + if (arg->neigh) + ffd_neigh_unref(arg->neigh); + + free(arg); + } +} + +static void update_request_ack(uint16_t nonce, void *argp) { + update_arg_t *arg = argp; + + fprintf(stderr, "debug: sending acked update with nonce %u.\n", nonce); + + ffd_update_t *update = ffd_send_update_new(NULL, arg->neigh, &nonce); + + ffd_announce_t *announce = ffd_announce_find(&arg->node, arg->type, arg->key); + + if (announce) + ffd_send_update_add(update, announce); + else + ffd_send_update_retract(update, arg->node, arg->type, arg->key); + + ffd_send_update_finish(update); +} + +static void update_request_ack_free(uint16_t nonce, void *arg) { + update_unref(arg); +} + +void ffd_update_enqueue(const ffd_node_id_t *node, uint16_t type, uint16_t key, ffd_neigh_t *neigh, bool urgent) { + if (neigh) + ffd_neigh_ref(neigh); + + update_arg_t *arg = update_new(node, type, key, neigh); + + if (urgent) { + if (neigh) { + ffd_ack_request(update_request_ack, update_request_ack_free, FFD_ACK_INTERVAL, 10, update_ref(arg)); + } + else { + int i; + for (i = 1; i <= 5; i++) + ffd_queue_put_delayed(&pending_updates, NULL, &now, i*FFD_URGENT_DELAY, update_ref(arg)); + } + } + else { + ffd_queue_put_delayed(&pending_updates, NULL, &now, FFD_DELAY, update_ref(arg)); + } +} + +int ffd_update_timeout(void) { + return ffd_queue_timeout(&pending_updates); +} + +void ffd_update_run(void) { + while (!ffd_update_timeout()) { + ffd_neigh_t *neigh = ((update_arg_t*)pending_updates->arg)->neigh; + + ffd_update_t *update = ffd_send_update_new(NULL, neigh, NULL); + + fprintf(stderr, "debug: sending scheduled updates.\n"); + + ffd_queue_t **entry; + for (entry = &pending_updates; *entry;) { + update_arg_t *arg = (*entry)->arg; + + if (arg->neigh != neigh) { + entry = &(*entry)->next; + continue; + } + + ffd_announce_t *announce = ffd_announce_find(&arg->node, arg->type, arg->key); + bool ret; + + if (announce) + ret = ffd_send_update_add(update, announce); + else + ret = ffd_send_update_retract(update, arg->node, arg->type, arg->key); + + if (!ret) + break; + + update_unref(arg); + ffd_queue_drop(entry); + } + + ffd_send_update_finish(update); + } +} diff --git a/ffd/util.h b/ffd/util.h index 9d481cb..0660b94 100644 --- a/ffd/util.h +++ b/ffd/util.h @@ -34,6 +34,9 @@ #include +extern struct timespec now; + + bool file_readv(const char *file, const char *format, va_list ap); void random_bytes(void *buffer, size_t len); @@ -53,6 +56,11 @@ static inline bool are_eth_addrs_equal(const eth_addr_t *address1, const eth_add return (a[0]==b[0] && a[1]==b[1] && a[2]==b[2] && a[3]==b[3] && a[4]==b[4] && a[5]==b[5]); } +static inline bool timespec_after(const struct timespec *tp1, const struct timespec *tp2) { + return (tp1->tv_sec > tp2->tv_sec || + (tp1->tv_sec == tp2->tv_sec && tp1->tv_nsec > tp2->tv_nsec)); +} + /* returns (tp1 - tp2) in centiseconds */ static inline int timespec_diff(const struct timespec *tp1, const struct timespec *tp2) { return ((tp1->tv_sec - tp2->tv_sec))*100 + (tp1->tv_nsec - tp2->tv_nsec)/1e7;