diff --git a/Makefile.dep b/Makefile.dep index 85076fee24bf..19ca72611d67 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -2,6 +2,13 @@ ifneq (,$(filter libcoap,$(USEPKG))) USEMODULE += pnet endif +ifneq (,$(filter olsr2,$(USEMODULE))) + USEMODULE += udp + USEMODULE += slist + USEMODULE += oonf_common + USEMODULE += oonf_rfc5444 +endif + ifneq (,$(filter pnet,$(USEMODULE))) USEMODULE += posix USEMODULE += socket_base diff --git a/core/include/kernel_macros.h b/core/include/kernel_macros.h index 8e75797906c3..40379e8ceb0d 100644 --- a/core/include/kernel_macros.h +++ b/core/include/kernel_macros.h @@ -16,6 +16,9 @@ * @author René Kijewski */ +#ifndef KERNEL_MACROS_ +#define KERNEL_MACROS_ + #include #ifdef __cplusplus @@ -35,6 +38,7 @@ extern "C" { * @param[in] MEMBER name of the member of TYPE which PTR points to * @return Pointer to the container of PTR. */ +#ifndef container_of #if __STDC_VERSION__ >= 201112L # define container_of(PTR, TYPE, MEMBER) \ (_Generic((PTR), \ @@ -53,11 +57,11 @@ extern "C" { # define container_of(PTR, TYPE, MEMBER) \ ((TYPE *) ((char *) (PTR) - offsetof(TYPE, MEMBER))) #endif - +#endif #ifdef __cplusplus } #endif - /** * @} */ +#endif /* KERNEL_MACROS_ */ diff --git a/examples/olsr2/Makefile b/examples/olsr2/Makefile new file mode 100644 index 000000000000..596ee1073837 --- /dev/null +++ b/examples/olsr2/Makefile @@ -0,0 +1,38 @@ +APPLICATION = olsr_node + +BOARD ?= native +RIOTBASE ?= $(CURDIR)/../.. + +# other toolchains lack assert.h used by oonf_api +BOARD_WHITELIST := avsextrem msba2 native + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +CFLAGS += -DDEVELHELP + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +USEPKG += oonf_api + +CFLAGS += -DRIOT -DENABLE_NAME + +# Modules to include. +USEMODULE += rtc +USEMODULE += uart0 +USEMODULE += posix +USEMODULE += ps +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += random +USEMODULE += config +USEMODULE += olsr2 +USEMODULE += defaulttransceiver + +# on msba2 and avsextrem cpuid_get is not implemented +ifneq (,$(filter $(BOARD), msba2 avsextrem)) + export CFLAGS += -DENABLE_LEDS -DHAVE_NO_CPUID +endif + +include $(RIOTBASE)/Makefile.include diff --git a/examples/olsr2/main.c b/examples/olsr2/main.c new file mode 100644 index 000000000000..78a6d9316aa0 --- /dev/null +++ b/examples/olsr2/main.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* Interface ID */ +#define IF_ID (0) + +#ifdef HAVE_NO_CPUID +#include + +static uint32_t get_node_id(void) { + return sysconfig.id; +} + +#else /* CPU ID availiable */ +#include + +static uint32_t get_node_id(void) { + uint32_t cpuid = 0; + cpuid_get(&cpuid); + return cpuid; +} + +#endif /* get_node_id */ + +#ifdef ENABLE_NAME + +static void ping(int argc, char **argv) { + static uint16_t id = 0; + + if (argc < 2) { + puts("usage: ping [node]"); + return; + } + + id++; + int packets = 10; + + ipv6_addr_t* dest = get_ip_by_name(argv[1]); + if (dest == NULL) { + printf("Unknown node: %s\n", argv[1]); + return; + } + + char addr_str[IPV6_MAX_ADDR_STR_LEN]; + ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, dest); + + uint8_t payload[] = "foobar"; + + for (int i = 0; i < packets; ++i) { + printf("sending %u bytes to %s\n", sizeof payload, addr_str); + icmpv6_send_echo_request(dest, id, i, payload, sizeof payload); + vtimer_usleep(1000000); + } +} +#endif /* ENABLE_NAME */ + +static void set_id(int argc, char **argv) { + if (argc < 2) { + puts("usage: set_id [id] [name]"); + return; + } + + uint16_t id = atoi(argv[1]); + sysconfig.id = id; + sysconfig.radio_address = (uint8_t) id; + +#ifdef ENABLE_NAME + if (argc > 2) + strncpy(sysconfig.name, argv[2], CONFIG_NAME_LEN); +#endif + config_save(); +} + +static void print_routes(__attribute__((unused)) int argc, __attribute__((unused)) char **argv) { + print_topology_set(); +} + +static void init(void) { + ipv6_addr_t tmp; + + rtc_enable(); + genrand_init(get_node_id()); + net_if_set_src_address_mode(IF_ID, NET_IF_TRANS_ADDR_M_SHORT); + net_if_set_hardware_address(IF_ID, get_node_id()); + + ipv6_addr_set_link_local_prefix(&tmp); + ipv6_addr_set_by_eui64(&tmp, IF_ID, &tmp); + ipv6_net_if_add_addr(IF_ID, &tmp, NDP_ADDR_STATE_PREFERRED, + NDP_OPT_PI_VLIFETIME_INFINITE, + NDP_OPT_PI_PLIFETIME_INFINITE, 0); + + ipv6_addr_set_all_nodes_addr(&tmp); + ipv6_net_if_add_addr(IF_ID, &tmp, NDP_ADDR_STATE_PREFERRED, + NDP_OPT_PI_VLIFETIME_INFINITE, + NDP_OPT_PI_PLIFETIME_INFINITE, 0); + + olsr_init(); +} + +const shell_command_t shell_commands[] = { + {"routes", "print all known nodes and routes", print_routes}, + {"set_id", "set node ID and name", set_id}, +#ifdef ENABLE_NAME + {"ping", "send packets to a node", ping}, +#endif + {NULL, NULL, NULL} +}; + +int main(void) { + init(); + + posix_open(uart0_handler_pid, 0); + + shell_t shell; + shell_init(&shell, shell_commands, UART0_BUFSIZE, uart0_readc, uart0_putc); + + shell_run(&shell); + + return 0; +} diff --git a/pkg/oonf_api/0005-only-define-container_of-when-necessary.patch b/pkg/oonf_api/0005-only-define-container_of-when-necessary.patch new file mode 100644 index 000000000000..56de15cee36b --- /dev/null +++ b/pkg/oonf_api/0005-only-define-container_of-when-necessary.patch @@ -0,0 +1,29 @@ +From d81d24b9d4c897c508799cb390b13cb018758709 Mon Sep 17 00:00:00 2001 +From: Benjamin Valentin +Date: Fri, 10 Oct 2014 02:05:01 +0200 +Subject: [PATCH] only define container_of when necessary + +--- + src-api/common/container_of.h | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src-api/common/container_of.h b/src-api/common/container_of.h +index 9fd1893..fcb38fe 100644 +--- a/src-api/common/container_of.h ++++ b/src-api/common/container_of.h +@@ -58,10 +58,12 @@ + * @param member name of node inside struct + * @return pointer to surrounding struct + */ ++#ifndef container_of + #define container_of(ptr, type, member) ({ \ + const typeof(((type *)0)->member ) *__tempptr = (ptr); \ + (type *)((char *)__tempptr - offsetof(type,member)); \ + }) ++#endif + + /** + * Helper function for NULL safe container_of macro +-- +1.9.1 + diff --git a/pkg/oonf_api/0006-if_index-is-not-used.patch b/pkg/oonf_api/0006-if_index-is-not-used.patch new file mode 100644 index 000000000000..1a26ea7a3833 --- /dev/null +++ b/pkg/oonf_api/0006-if_index-is-not-used.patch @@ -0,0 +1,25 @@ +From 40651f114bd6e1b4b2ebc89bdf8fb06d1243eb55 Mon Sep 17 00:00:00 2001 +From: Benjamin Valentin +Date: Fri, 10 Oct 2014 02:08:32 +0200 +Subject: [PATCH] if_index is not used + +--- + src-api/common/netaddr.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src-api/common/netaddr.c b/src-api/common/netaddr.c +index ed44341..fa528ca 100644 +--- a/src-api/common/netaddr.c ++++ b/src-api/common/netaddr.c +@@ -319,7 +319,7 @@ netaddr_create_host_bin(struct netaddr *host, const struct netaddr *netmask, + */ + int + netaddr_socket_init(union netaddr_socket *combined, const struct netaddr *addr, +- uint16_t port, unsigned if_index) { ++ uint16_t port, unsigned if_index __attribute__((unused))) { + /* initialize memory block */ + memset(combined, 0, sizeof(*combined)); + +-- +1.9.1 + diff --git a/pkg/oonf_api/0007-Use-RIOT-s-container_of-implementation.patch b/pkg/oonf_api/0007-Use-RIOT-s-container_of-implementation.patch new file mode 100644 index 000000000000..94f41df128f4 --- /dev/null +++ b/pkg/oonf_api/0007-Use-RIOT-s-container_of-implementation.patch @@ -0,0 +1,45 @@ +From b2ad2073ac282f1bc6315e47ffbd12c3f6a9ae1a Mon Sep 17 00:00:00 2001 +From: Hinnerk van Bruinehsen +Date: Wed, 29 Oct 2014 11:37:05 +0100 +Subject: [PATCH] Use RIOT's container_of implementation + +--- + src-api/common/container_of.h | 22 ++++++++++++++++++---- + 1 file changed, 18 insertions(+), 4 deletions(-) + +diff --git a/src-api/common/container_of.h b/src-api/common/container_of.h +index fcb38fe..b49d836 100644 +--- a/src-api/common/container_of.h ++++ b/src-api/common/container_of.h +@@ -59,10 +59,24 @@ + * @return pointer to surrounding struct + */ + #ifndef container_of +-#define container_of(ptr, type, member) ({ \ +- const typeof(((type *)0)->member ) *__tempptr = (ptr); \ +- (type *)((char *)__tempptr - offsetof(type,member)); \ +- }) ++#if __STDC_VERSION__ >= 201112L ++# define container_of(PTR, TYPE, MEMBER) \ ++ (_Generic((PTR), \ ++ const __typeof__ (((TYPE *) 0)->MEMBER) *: \ ++ ((TYPE *) ((char *) (PTR) - offsetof(TYPE, MEMBER))), \ ++ __typeof__ (((TYPE *) 0)->MEMBER) *: \ ++ ((TYPE *) ((char *) (PTR) - offsetof(TYPE, MEMBER))) \ ++ )) ++#elif defined __GNUC__ ++# define container_of(PTR, TYPE, MEMBER) \ ++ (__extension__ ({ \ ++ __extension__ const __typeof__ (((TYPE *) 0)->MEMBER) *__m____ = (PTR); \ ++ ((TYPE *) ((char *) __m____ - offsetof(TYPE, MEMBER))); \ ++ })) ++#else ++# define container_of(PTR, TYPE, MEMBER) \ ++ ((TYPE *) ((char *) (PTR) - offsetof(TYPE, MEMBER))) ++#endif + #endif + + /** +-- +2.1.2 + diff --git a/pkg/oonf_api/0008-Dissolve-enum-into-single-defines.patch b/pkg/oonf_api/0008-Dissolve-enum-into-single-defines.patch new file mode 100644 index 000000000000..536df16007bf --- /dev/null +++ b/pkg/oonf_api/0008-Dissolve-enum-into-single-defines.patch @@ -0,0 +1,54 @@ +From e590e6f26b115da34a943fd4ed6d4c93fd2c64d0 Mon Sep 17 00:00:00 2001 +From: Hinnerk van Bruinehsen +Date: Wed, 29 Oct 2014 12:05:11 +0100 +Subject: [PATCH] Dissolve enum into single defines + +--- + src-api/rfc5444/rfc5444.h | 26 ++++++++++++-------------- + 1 file changed, 12 insertions(+), 14 deletions(-) + +diff --git a/src-api/rfc5444/rfc5444.h b/src-api/rfc5444/rfc5444.h +index c5d6420..6b5576e 100644 +--- a/src-api/rfc5444/rfc5444.h ++++ b/src-api/rfc5444/rfc5444.h +@@ -43,25 +43,23 @@ + + #include "common/common_types.h" + +-enum { +- /* timetlv_max = 14 * 2^28 * 1000 / 1024 = 14000 << 18 = 3 670 016 000 ms */ +- RFC5444_TIMETLV_MAX = 0xdac00000, ++/* timetlv_max = 14 * 2^28 * 1000 / 1024 = 14000 << 18 = 3 670 016 000 ms */ ++#define RFC5444_TIMETLV_MAX 0xdac00000 + +- /* timetlv_min = 1000/1024 ms */ +- RFC5444_TIMETLV_MIN = 0x00000001, ++/* timetlv_min = 1000/1024 ms */ ++#define RFC5444_TIMETLV_MIN 0x00000001 + +- /* metric_max = 1<<24 - 256 */ +- RFC5444_METRIC_MAX = 0xffff00, ++/* metric_max = 1<<24 - 256 */ ++#define RFC5444_METRIC_MAX 0xffff00 + +- /* metric_min = 1 */ +- RFC5444_METRIC_MIN = 0x000001, ++/* metric_min = 1 */ ++#define RFC5444_METRIC_MIN 0x000001 + +- /* larger than possible metric value */ +- RFC5444_METRIC_INFINITE = 0xffffff, ++/* larger than possible metric value */ ++#define RFC5444_METRIC_INFINITE 0xffffff + +- /* infinite path cost */ +- RFC5444_METRIC_INFINITE_PATH = 0xffffffff, +-}; ++/* infinite path cost */ ++#define RFC5444_METRIC_INFINITE_PATH 0xffffffff + + EXPORT uint8_t rfc5444_timetlv_get_from_vector( + uint8_t *vector, size_t vector_length, uint8_t hopcount); +-- +2.1.2 + diff --git a/pkg/oonf_api/0009-Add-missing-include.patch b/pkg/oonf_api/0009-Add-missing-include.patch new file mode 100644 index 000000000000..444bfcd4bffa --- /dev/null +++ b/pkg/oonf_api/0009-Add-missing-include.patch @@ -0,0 +1,24 @@ +From 21202804f26b194607a412476a96f03d3df30688 Mon Sep 17 00:00:00 2001 +From: Hinnerk van Bruinehsen +Date: Wed, 29 Oct 2014 12:11:29 +0100 +Subject: [PATCH 9/9] Add missing include + +--- + src-api/rfc5444/rfc5444_tlv_writer.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src-api/rfc5444/rfc5444_tlv_writer.h b/src-api/rfc5444/rfc5444_tlv_writer.h +index ace7313..8d0ce3a 100644 +--- a/src-api/rfc5444/rfc5444_tlv_writer.h ++++ b/src-api/rfc5444/rfc5444_tlv_writer.h +@@ -43,6 +43,7 @@ + #define RFC5444_TLV_WRITER_H_ + + #include "common/common_types.h" ++#include "rfc5444_context.h" + + struct rfc5444_tlv_writer_data { + uint8_t *buffer; +-- +2.1.2 + diff --git a/pkg/oonf_api/0010-Change-index-of-array-from-0-to-1.patch b/pkg/oonf_api/0010-Change-index-of-array-from-0-to-1.patch new file mode 100644 index 000000000000..3deebdab4e85 --- /dev/null +++ b/pkg/oonf_api/0010-Change-index-of-array-from-0-to-1.patch @@ -0,0 +1,25 @@ +From 0ecbb8a8b896ac4aff57d94d678a4a95084a095c Mon Sep 17 00:00:00 2001 +From: Hinnerk van Bruinehsen +Date: Wed, 29 Oct 2014 12:13:27 +0100 +Subject: [PATCH 10/10] Change index of array from 0 to 1 + +--- + src-api/common/template.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src-api/common/template.h b/src-api/common/template.h +index d98fe77..7ca75a8 100644 +--- a/src-api/common/template.h ++++ b/src-api/common/template.h +@@ -64,7 +64,7 @@ struct abuf_template_storage_entry { + + struct abuf_template_storage { + size_t count; +- struct abuf_template_storage_entry indices[0]; ++ struct abuf_template_storage_entry indices[1]; + }; + + EXPORT struct abuf_template_storage *abuf_template_init ( +-- +2.1.2 + diff --git a/sys/Makefile b/sys/Makefile index 246190413480..e98dd5d4b38a 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -10,6 +10,21 @@ endif ifneq (,$(filter shell_commands,$(USEMODULE))) DIRS += shell/commands endif +ifneq (,$(filter slist,$(USEMODULE))) + DIRS += slist +endif +ifneq (,$(filter timex,$(USEMODULE))) + DIRS += timex +endif +ifneq (,$(filter transceiver,$(USEMODULE))) + DIRS += transceiver +endif +ifneq (,$(filter uart0,$(USEMODULE))) + DIRS += uart0 +endif +ifneq (,$(filter vtimer,$(USEMODULE))) + DIRS += vtimer +endif ifneq (,$(filter net_if,$(USEMODULE))) DIRS += net/link_layer/net_if endif @@ -44,6 +59,9 @@ endif ifneq (,$(filter rpl,$(USEMODULE))) DIRS += net/routing/rpl endif +ifneq (,$(filter olsr2,$(USEMODULE))) + DIRS += net/routing/olsr2 +endif ifneq (,$(filter routing,$(USEMODULE))) DIRS += net/routing endif diff --git a/sys/include/slist.h b/sys/include/slist.h new file mode 100644 index 000000000000..f584819f0fc1 --- /dev/null +++ b/sys/include/slist.h @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @file slist.h + * @brief simple single linked list implementation with iterators + * + * A list entry is a struct of an arbitrary type but with the only constraint that + * it's first entry is a pointer of the same type as the struct with the name next. + * + * A list is defined by it's first entry, called head. Since the first entry may + * change, a pointer to the first entry is used to refer to the list. + * + * The list automatically allocates and deallocates memory when list elements + * are added or removed + * + * @author Benjamin Valentin + */ + + +#ifndef SIMPLE_LIST_H_ +#define SIMPLE_LIST_H_ +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct simple_list_elem; + +/** + * @brief allocates memory for a new list entry and appends it before the head. The new entry is the new head. + * + * @param head pointer to the list + * @return the new list entry, NULL if no new list entry could be allocated + */ +#define simple_list_add_head(head) __simple_list_add_head((struct simple_list_elem**) (head), calloc(1, sizeof **(head))) + + +/** + * @brief appends a preallocated element to the top of the list. The new entry is the new head. + * + * @param head pointer to the list + * @param node preallocated list element + * @return the new list entry (node) + */ +#define simple_list_set_head(head, node) __simple_list_add_head((struct simple_list_elem**) (head), (node)) + +/** +* @brief allocates memory for a new list entry and appends it at the end of the list. +* +* @param head pointer to pointer to the first list element +* @param head pointer to the list +* @return the new list entry, NULL if no new list entry could be allocated +*/ +#define simple_list_add_tail(head) __simple_list_add_tail((struct simple_list_elem**) (head), calloc(1, sizeof **(head))) + +/** + * @brief appends a preallocated element to the end of the list. + * + * @param head pointer to the list + * @param node preallocated list element + * @return the new list entry (node) + */ +#define simple_list_set_tail(head, node) __simple_list_add_tail((struct simple_list_elem**) (head), (node)) + +/** +* @brief allocates memory for a new list entry and adds it before an existing entry. +* The new entry is added before the existing element where old_entry->value > value +* If no such entry could be found, the new entry will be added at the end of the list +* +* @param head pointer to the list +* @param value value to compare list entries +* has to be the same name as the element in the list entry structure that is be used for comparison +* @return pointer to the new element, NULL if no new list element could be allocated +*/ +#define simple_list_add_before(head, value) (\ + *(head) == NULL ? \ + simple_list_add_head((head)) \ + : \ + __simple_list_add_before((struct simple_list_elem**) (head), \ + calloc(1, sizeof **(head)), \ + (value), \ + (char*) &(*(head))->value - (char*) *(head)) ) + +/** +* @brief adds an preallocated list element before an existing one. +* The new entry is added before the existing element where old_entry->value > value +* If no such entry could be found, the new entry will be added at the end of the list * +* @param head pointer to the list +* @param value value to compare list entries +* has to be the same name as the element in the list entry structure that is be used for comparison +* @param node preallocated list element +* @return the new list entry (node) +*/ +#define simple_list_set_before(head, node, value) (\ + *(head) == NULL ? \ + __simple_list_add_before((head)) \ + : \ + __simple_list_add_before((struct simple_list_elem**) (head), \ + (node), \ + (value), \ + (char*) &(*(head))->value - (char*) *(head)) ) + +/** +* @brief searches for a list element by simple comparison of a struct value +* +* @param head pointer to the list +* @param value the member value of a list entry that is to be found +* has to be the same name as the value in the list element struct +* @return pointer the list entry if found, otherwise NULL +*/ +#define simple_list_find(head, value) (\ + (head) == NULL ? \ + NULL \ + : \ + __simple_list_find( (struct simple_list_elem*) (head), \ + (value), \ + (char*) &((head)->value) - (char*) (head), \ + (0)) ) + +/** +* @brief searches for a list element by comparing a buffer in the list element struct +* +* @param head pointer to the list +* @param value pointer to the buffer that is to be found in the list +* has to be the same name as the value in the list element struct +* @return pointer the list entry if found, otherwise NULL +*/ +#define simple_list_find_memcmp(head, value) (\ + (head) == NULL ? \ + NULL \ + : \ + __simple_list_find( (struct simple_list_elem*) (head), \ + (value), \ + (char*) &((head)->value) - (char*) (head), \ + sizeof(*(value))) ) + +/** +* @brief searches for a list element by applying a comparator function to each list entry +* +* @param head pointer to the list +* @param value input to the comparator function +* @param comperator a function that takes (value, node) and returns 0 if they match +* @return pointer the list entry if found, otherwise NULL +*/ +#define simple_list_find_cmp(head, value, comperator) (\ + (head) == NULL ? \ + NULL \ + : \ + __simple_list_find_cmp( (struct simple_list_elem*) (head), \ + (value), \ + (char*) &((head)->value) - (char*) (head), \ + (comperator)) ) +/** + * @brief removes an entry from the list and frees it's memory + * + * @param head pointer to the list + * @param node entry to be removed + * @returns a non-zero value if the element was found and removed + */ +#define simple_list_remove(head, node) __simple_list_remove((struct simple_list_elem**) (head), (struct simple_list_elem*) (node), 0) + +/** + * @brief removes an entry from the list, doesn't free it's memory but returns the element + * + * @param head pointer to the list + * @param node entry to be extracted + * @returns pointer to the element, NULL if it couldn't be found + */ +#define simple_list_extract(head, node) __simple_list_remove((struct simple_list_elem**) (head), (struct simple_list_elem*) (node), 1) + +/** + * @brief removes all entries from the list and frees their memory + * + * @param head pointer to the list + */ +#define simple_list_clear(head) __simple_list_clear((struct simple_list_elem**) (head)) + +/** + * @brief starts a loop to iterate over all list entries. Read-only list access only. + * needs to be provided with a local loop variable + * + * @param head pointer to the list + * @param node to the current entry (loop variable) + */ +#define simple_list_for_each(head, node) \ + for ((node) = (head); (node); (node) = (node)->next) + +/** + * @brief starts a loop to iterate over all list elements with the possibility to remove elements + to remove an element, use simple_list_for_each_remove + needs to be provided with a local loop variable as well as two local auxiliary variables + * + * @param head pointer to the list + * @param node to the current entry (loop variable) + * @param prev internal variable, pointer to previous list entry - do not modify + * @param skipped internal variable, integer - do not modify + */ +#define simple_list_for_each_safe(head, node, prev, skipped) \ + for ((skipped) = 0, (prev) = NULL, (node) = (head); \ + (node); \ + (prev) = ((skipped) ? (prev) : (node)), \ + (node) = ((skipped) ? (node) : (node)->next), (skipped) = 0) + +/** + * @brief removes an element in a simple_list_for_each_safe context + * + * @param head pointer to the list + * @param node pointer to the current entry (loop variable) + * @param prev internal variable, provided by simple_list_for_each_safe + */ +#define simple_list_for_each_remove(head, node, prev) \ + do { \ + if (!(prev)) { \ + (skipped) = 1; \ + *(head) = (*(head))->next; \ + } else { \ + (prev)->next = (node)->next; \ + } \ + free(node); \ + (node) = (prev) ? (prev) : *(head); \ + } while (0) + +void *__simple_list_add_head(struct simple_list_elem **head, void *mem); +void *__simple_list_add_tail(struct simple_list_elem **head, void *mem); +void *__simple_list_add_before(struct simple_list_elem **head, void *mem, int needle, size_t offset); +void *__simple_list_find(struct simple_list_elem *head, void *needle, size_t offset, size_t size); +void *__simple_list_find_cmp(struct simple_list_elem *head, void *needle, size_t offset, int compare(void *, void *)); +void *__simple_list_remove(struct simple_list_elem **head, struct simple_list_elem *node, int keep); +void __simple_list_clear(struct simple_list_elem **head); + +#ifdef __cplusplus +} +#endif + +#endif /* SIMPLE_LIST_H_ */ diff --git a/sys/net/include/olsr2/olsr2.h b/sys/net/include/olsr2/olsr2.h new file mode 100644 index 000000000000..595936a32ef8 --- /dev/null +++ b/sys/net/include/olsr2/olsr2.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @brief The OLSRv2 routing algorithm. + * @author Benjamin Valentin + * @} + */ + + +#include "sixlowpan/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Start routing using olsr2 + * This initializes and starts all neccecary + * background threads for OLSRv2 routing, control + * messages will be send and received on MANET_PORT + */ +void olsr_init(void); + +/** + * @brief prints all known routers of the MANET + */ +void print_topology_set(void); + +#ifdef ENABLE_NAME +/** + * @brief get the IP address of a MANET router from its hostname + */ +ipv6_addr_t* get_ip_by_name(char* name); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sys/net/routing/olsr2/Makefile b/sys/net/routing/olsr2/Makefile new file mode 100644 index 000000000000..1ead3177295b --- /dev/null +++ b/sys/net/routing/olsr2/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/routing/olsr2/constants.h b/sys/net/routing/olsr2/constants.h new file mode 100644 index 000000000000..50ed94ce83b6 --- /dev/null +++ b/sys/net/routing/olsr2/constants.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @author Benjamin Valentin + * @} + */ + +#ifndef OLSR2_CONSTANTS_H_ +#define OLSR2_CONSTANTS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* The well-known UDP port for MANET as defined in RFC 5498 */ +#define MANET_PORT 269 + +/* in seconds */ +#define OLSR2_HELLO_REFRESH_INTERVAL_SECONDS 2 +#define OLSR2_TC_REFRESH_INTERVAL_SECONDS 5 +#define OLSR2_HOLD_TIME_SECONDS (3 * OLSR2_TC_REFRESH_INTERVAL_SECONDS) + +#define OLSR2_TC_HOP_LIMIT 16 + +#define OLSR2_HYST_SCALING 0.4 +#define OLSR2_HYST_LOW 0.3 +#define OLSR2_HYST_HIGH 0.8 + +/* in µs */ +#define SECOND (1000 * 1000) +#define OLSR2_MAX_JITTER_US SECOND + +#define OLSR2_FLOODING_MPR_SELECTOR 1 +#define OLSR2_ROUTING_MPR_SELECTOR 2 + +#define RFC5444_TLV_NODE_NAME 42 + +/* NHDP message TLV array index */ +enum { + IDX_TLV_ITIME, /* Interval time */ + IDX_TLV_VTIME, /* validity time */ + IDX_TLV_NODE_NAME, /* name of the node */ +}; + +/* NHDP address TLV array index */ +enum { + IDX_ADDRTLV_LOCAL_IF, /* is local if */ + IDX_ADDRTLV_LINK_STATUS, /* link status TODO */ + IDX_ADDRTLV_MPR, /* neighbor selected as mpr */ + IDX_ADDRTLV_METRIC, /* incomming link metric */ + IDX_ADDRTLV_NODE_NAME, /* 'name' of a node from graph.gv */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* OLSR2_CONSTANTS_H_ */ diff --git a/sys/net/routing/olsr2/nhdp.c b/sys/net/routing/olsr2/nhdp.c new file mode 100644 index 000000000000..b452a12531e4 --- /dev/null +++ b/sys/net/routing/olsr2/nhdp.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @author Benjamin Valentin + * @} + */ + +#include + +#include "nhdp.h" +#include "util.h" +#include "debug.h" +#include "node.h" +#include "routing_table.h" +#include "constants.h" + +#include "common/avl.h" + +#ifdef ENABLE_DEBUG +static struct netaddr_str nbuf[1]; +#endif + +static struct olsr_node *_node_replace(struct olsr_node *old_n) +{ + struct olsr_node *new_n = calloc(1, sizeof(struct nhdp_node)); + + if (new_n == NULL) { + return old_n; + } + + /* remove things that held a pointer to this */ + avl_remove(get_olsr_head(), &old_n->node); + bool _free_node = remove_free_node(old_n); + + memcpy(new_n, old_n, sizeof(struct olsr_node)); + memset(&new_n->node, 0, sizeof(new_n->node)); + + new_n->type = NODE_TYPE_NHDP; + new_n->node.key = new_n->addr; + avl_insert(get_olsr_head(), &new_n->node); + + free(old_n); + + if (_free_node) { + add_free_node(new_n); + } + + new_n->pending = 1; + h1_deriv(new_n)->link_quality = OLSR2_HYST_SCALING; + + return new_n; +} + +struct olsr_node *olsr2_add_neighbor(struct netaddr *addr, metric_t metric, uint8_t vtime, char *name) +{ + struct olsr_node *n = get_node(addr); + + if (n == NULL) { + DEBUG("\tadding new neighbor: %s", netaddr_to_str_s(&nbuf[0], addr)); + n = calloc(1, sizeof(struct nhdp_node)); + + if (n == NULL) { + return NULL; + } + + n->addr = netaddr_dup(addr); + + if (n->addr == NULL) { + free(n); + return NULL; + } + + n->type = NODE_TYPE_NHDP; + n->distance = 1; + n->link_metric = metric; + h1_deriv(n)->link_quality = OLSR2_HYST_SCALING; + n->pending = 1; + +#ifdef ENABLE_NAME + + if (name != NULL) { + n->name = strdup(name); + } + +#endif + + n->node.key = n->addr; + avl_insert(get_olsr_head(), &n->node); + } + else if (n->type != NODE_TYPE_NHDP) { + DEBUG("\tconverting olsr node %s to nhdp node", + netaddr_to_str_s(&nbuf[0], n->addr)); + n = _node_replace(n); + } + + /* add_other_route would otherwise not update expires */ + if (n->next_addr == NULL) { + n->expires = time_now() + vtime; + } + + add_other_route(n, get_local_addr(), 1, metric, vtime); + + return n; +} diff --git a/sys/net/routing/olsr2/nhdp.h b/sys/net/routing/olsr2/nhdp.h new file mode 100644 index 000000000000..61c854584dcd --- /dev/null +++ b/sys/net/routing/olsr2/nhdp.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @author Benjamin Valentin + * @} + */ + +#ifndef NHDP_H_ +#define NHDP_H_ + +#include "common/avl.h" +#include "common/netaddr.h" + +#include "node.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct olsr_node *olsr2_add_neighbor(struct netaddr *addr, metric_t metric, uint8_t vtime, char *name); + +#ifdef __cplusplus +} +#endif + +#endif /* NHDP_H_ */ diff --git a/sys/net/routing/olsr2/node.c b/sys/net/routing/olsr2/node.c new file mode 100644 index 000000000000..1cec09030eef --- /dev/null +++ b/sys/net/routing/olsr2/node.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @author Benjamin Valentin + * @} + */ + +#include + +#include "node.h" +#include +#include "debug.h" + +#include "common/netaddr.h" +#include "rfc5444/rfc5444.h" + +static struct netaddr_rc local_addr; +static struct avl_tree olsr_head; + +#ifdef ENABLE_DEBUG +static struct netaddr_str nbuf[2]; +#endif + +#ifdef ENABLE_NAME +char *olsr2_local_name; +#endif + +static void _decrease_mpr_neigh(struct olsr_node *node) +{ + DEBUGF("%s (%s)", netaddr_to_str_s(&nbuf[0], node->addr), node->name); + + /* only consider 2-hop nieghbors (only 2-hop neighbors have flood_mpr set) */ + if (node->flood_mpr == NULL) { + return; + } + + /* update routing MPR information */ + struct nhdp_node *n1 = h1_deriv(get_node(node->next_addr)); + + if (n1 != NULL && n1->mpr_neigh_route > 0) { + n1->mpr_neigh_route--; + } + + /* update flooding MPR information */ + struct nhdp_node *n1_f = h1_deriv(get_node(node->flood_mpr)); + + if (n1_f != NULL && n1_f->mpr_neigh_flood > 0) { + n1_f->mpr_neigh_flood--; + } +} + +static int _addr_cmp(const void *a, const void *b) +{ + return memcmp(a, b, NETADDR_MAX_LENGTH); +} + +int olsr_node_cmp(struct olsr_node *a, struct olsr_node *b) +{ + return netaddr_cmp(a->addr, b->addr); +} + +void node_init(void) +{ + local_addr._refs = 1; + avl_init(get_olsr_head(), _addr_cmp, false); +} + +struct netaddr *get_local_addr(void) +{ + return (struct netaddr *) &local_addr; +} + +struct avl_tree *get_olsr_head(void) +{ + return &olsr_head; +} + +struct olsr_node *get_node(struct netaddr *addr) +{ + struct olsr_node *n; // for typeof + + if (addr == NULL) { + return NULL; + } + + return avl_find_element(get_olsr_head(), addr, n, node); +} + +metric_t get_link_metric(struct olsr_node *node, struct netaddr *last_addr) +{ + if (node->last_addr != NULL && netaddr_cmp(node->last_addr, last_addr) == 0) { + return node->link_metric; + } + + struct alt_route *route = simple_list_find_memcmp(node->other_routes, last_addr); + + if (route == NULL) { + return RFC5444_METRIC_INFINITE; + } + + return route->link_metric; +} + +void add_other_route(struct olsr_node *node, struct netaddr *last_addr, uint8_t distance, metric_t metric, uint8_t vtime) +{ + /* make sure the route is not already the default route */ + if (node->last_addr != NULL && netaddr_cmp(node->last_addr, last_addr) == 0) { + if (node->next_addr != NULL) { + // TODO: a different route might be better now + node->path_metric -= node->link_metric - metric; + node->link_metric = metric; + } + + node->expires = time_now() + vtime; + return; + } + + struct alt_route *route = simple_list_find_memcmp(node->other_routes, last_addr); + + if (route != NULL) { + route->expires = time_now() + vtime; + route->link_metric = metric; + return; + } + + route = simple_list_add_head(&node->other_routes); + + if (route == NULL) { + printf("ERROR: out of memory in %s\n", __FUNCTION__); + return; + } + + route->last_addr = netaddr_reuse(last_addr); + route->expires = time_now() + vtime; + route->link_metric = metric; + + /* if we add a route for the first time, increment flood_neighbors */ + if (distance == 2 && node->type != NODE_TYPE_NHDP) { + struct nhdp_node *n1 = h1_deriv(get_node(last_addr)); + + if (n1 != NULL) { + n1->flood_neighbors++; + } + } + +} + +void remove_default_node(struct olsr_node *node) +{ + if (node->last_addr) { + _decrease_mpr_neigh(node); + + struct nhdp_node *mpr = h1_deriv(get_node(node->last_addr)); + + if (mpr != NULL) { + mpr->flood_neighbors--; + } + + node->last_addr = netaddr_free(node->last_addr); + } + + node->next_addr = netaddr_free(node->next_addr); +} + +/* + * moves the default route of node to other_routes + */ +void push_default_route(struct olsr_node *node) +{ + struct netaddr *last_addr = node->last_addr; + + if (node->last_addr == NULL) { + return; + } + + _decrease_mpr_neigh(node); + struct alt_route *route = simple_list_find_memcmp(node->other_routes, last_addr); + + /* don't add route if it already exists - this should never happen, right? */ + if (route != NULL) { + node->last_addr = netaddr_free(node->last_addr); + return; + } + + route = simple_list_add_head(&node->other_routes); + + if (route == NULL) { + printf("ERROR: out of memory in %s\n", __FUNCTION__); + return; + } + + route->expires = node->expires; + route->last_addr = node->last_addr; + route->link_metric = node->link_metric; + node->last_addr = NULL; +} + +void pop_other_route(struct olsr_node *node, struct netaddr *last_addr) +{ + char skipped; + struct alt_route *route, *prev; + simple_list_for_each_safe(node->other_routes, route, prev, skipped) { + if (netaddr_cmp(route->last_addr, last_addr)) { + continue; + } + + node->last_addr = route->last_addr; + node->expires = route->expires; + node->link_metric = route->link_metric; + simple_list_for_each_remove(&node->other_routes, route, prev); + break; + } +} + +void remove_other_route(struct olsr_node *node, struct netaddr *last_addr) +{ + DEBUGF("%s (%s), %s", netaddr_to_str_s(&nbuf[0], node->addr), node->name, + netaddr_to_str_s(&nbuf[1], last_addr)); + + char skipped; + struct alt_route *route, *prev; + simple_list_for_each_safe(node->other_routes, route, prev, skipped) { + if (netaddr_cmp(route->last_addr, last_addr)) { + continue; + } + + struct nhdp_node *mpr = h1_deriv(get_node(route->last_addr)); + + if (mpr != NULL) { + mpr->flood_neighbors--; + } + + netaddr_free(route->last_addr); + simple_list_for_each_remove(&node->other_routes, route, prev); + break; + } +} diff --git a/sys/net/routing/olsr2/node.h b/sys/net/routing/olsr2/node.h new file mode 100644 index 000000000000..8cc66723e088 --- /dev/null +++ b/sys/net/routing/olsr2/node.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @author Benjamin Valentin + * @} + */ + +#ifndef NODE_H_ +#define NODE_H_ + +#include "common/avl.h" +#include "common/netaddr.h" + +#include "util.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* nodes can have a name (hostname) for easier debugging */ +#ifdef ENABLE_NAME +extern char *olsr2_local_name; +#endif + +/* if a connection is lost, the loss will be reported LOST_ITER_MAX times in HELLO and TC messages. */ +#define LOST_ITER_MAX (1 << 3) + +enum { + NODE_TYPE_OLSR, + NODE_TYPE_NHDP +}; + +typedef uint32_t metric_t; + +/* simple list to store alternative routes */ +struct alt_route { + struct alt_route *next; + + struct netaddr *last_addr; + metric_t link_metric; + time_t expires; +}; + +struct olsr_node { + struct avl_node node; /* for routing table Information Base */ + + struct netaddr *addr; /* node address */ + struct netaddr *next_addr; /* neighbor addr to send packets to for this node*/ + struct netaddr *last_addr; /* node that announced this node */ + struct alt_route *other_routes; /* other possible last_addrs */ + + time_t expires; /* time when this tuple is invalid */ + uint16_t seq_no; /* last seq_no from last_addr */ + uint8_t distance; /* hops between us and the node */ + metric_t link_metric; + metric_t path_metric; + struct netaddr *flood_mpr; /* flooding MPR to broadcast to this node (used for couting mpr_neigh_flood), 2-hop only */ + + uint8_t type : 1; /* node type */ + uint8_t pending : 1; /* whether the link can already be used - only 1-hop */ + uint8_t lost : 4; /* [4 bit] if set, the node will be annouced as lost - only 1-hop */ + +#ifdef ENABLE_NAME + char *name; /* node name from graph.gv */ +#endif +}; + +struct nhdp_node { + struct olsr_node super; + + uint8_t mpr_slctr_flood: 1; /* whether the node selected us as a flooding MPR */ + + uint8_t mpr_slctr_route: 1; /* whether the node selected us as a routing MPR */ + + /* number of --hop neighbors broadcast messages from this this node can reach */ + uint8_t flood_neighbors; + + /* number of 2-hop neighbors reached if this node is used as flooding MPR */ + uint8_t mpr_neigh_flood; + + /* number of 2-hop neighbors reached through this node aka if this value is > 0, it's a routing MPR */ + uint8_t mpr_neigh_route; + + /* average packet loss, decides if it should be used as 1-hop neigh */ + float link_quality; +}; + +static inline struct olsr_node *h1_super(struct nhdp_node *n) +{ + return (struct olsr_node *) n; +} +static inline struct nhdp_node *h1_deriv(struct olsr_node *n) +{ + if (n == NULL) { + return 0; + } + + if (n->type != NODE_TYPE_NHDP) { + return 0; + } + + return (struct nhdp_node *) n; +} + +void node_init(void); +struct netaddr *get_local_addr(void); +struct avl_tree *get_olsr_head(void); +int olsr_node_cmp(struct olsr_node *a, struct olsr_node *b); +struct olsr_node *get_node(struct netaddr *addr); +metric_t get_link_metric(struct olsr_node *node, struct netaddr *last_addr); + +void add_other_route(struct olsr_node *node, struct netaddr *last_addr, uint8_t distance, metric_t metric, uint8_t vtime); +void remove_other_route(struct olsr_node *node, struct netaddr *last_addr); +void remove_default_node(struct olsr_node *node); +void push_default_route(struct olsr_node *node); +void pop_other_route(struct olsr_node *node, struct netaddr *last_addr); + +#ifdef __cplusplus +} +#endif + +#endif /* NODE_H_ */ diff --git a/sys/net/routing/olsr2/olsr.c b/sys/net/routing/olsr2/olsr.c new file mode 100644 index 000000000000..bbb5bd61213a --- /dev/null +++ b/sys/net/routing/olsr2/olsr.c @@ -0,0 +1,505 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @author Benjamin Valentin + * @} + */ + +#include +#include + +#ifdef ENABLE_NAME +#include +#endif + +#include "common/netaddr.h" +#include "rfc5444/rfc5444.h" + +#include "olsr.h" +#include "util.h" +#include "debug.h" +#include "routing_table.h" +#include "constants.h" +#include + +#ifdef ENABLE_DEBUG +static struct netaddr_str nbuf[3]; +#endif + +static struct olsr_node *_new_olsr_node(struct netaddr *addr, + uint8_t distance, metric_t metric, uint8_t vtime, char *name) +{ + + struct olsr_node *n = calloc(1, sizeof(struct olsr_node)); + + if (n == NULL) { + return NULL; + } + + n->addr = netaddr_dup(addr); + + if (n->addr == NULL) { + free(n); + return NULL; + } + + n->node.key = n->addr; + n->type = NODE_TYPE_OLSR; + n->distance = distance; + n->link_metric = metric; + n->expires = time_now() + vtime; +#ifdef ENABLE_NAME + + if (name) { + n->name = strdup(name); + } + +#endif + + avl_insert(get_olsr_head(), &n->node); + return n; +} + +static void _get_new_flood_mpr(struct netaddr *old_flood_mpr) +{ + DEBUGF("%s", netaddr_to_str_s(&nbuf[0], old_flood_mpr)); + struct olsr_node *node; + avl_for_each_element(get_olsr_head(), node, node) { + if (node->distance != 2) { + continue; + } + + if (node->flood_mpr != NULL && netaddr_cmp(old_flood_mpr, node->flood_mpr) != 0) { + continue; + } + + DEBUG("chosing new flood MPR for %s (%s)", netaddr_to_str_s(&nbuf[0], node->addr), node->name); + + struct nhdp_node *mpr_b, *mpr_a = h1_deriv(get_node(node->last_addr)); + struct alt_route *route; + simple_list_for_each(node->other_routes, route) { + mpr_b = h1_deriv(get_node(route->last_addr)); + + if (mpr_b == NULL || h1_super(mpr_b)->pending) { + continue; + } + + if (mpr_a == NULL || h1_super(mpr_a)->pending || mpr_a->flood_neighbors < mpr_b->flood_neighbors) { + mpr_a = mpr_b; + } + } + + if (mpr_a != NULL) { + mpr_a->mpr_neigh_flood++; + + netaddr_switch(&node->flood_mpr, h1_super(mpr_a)->addr); + DEBUG("[%s] setting flood MPR to %s", __FUNCTION__, netaddr_to_str_s(&nbuf[0], node->flood_mpr)); + } + else { + node->flood_mpr = netaddr_free(node->flood_mpr); + } + + DEBUG("\tnew flood MPR: %s", netaddr_to_str_s(&nbuf[0], node->flood_mpr)); + } +} + +/* + * find a new route for nodes that use last_addr as their default route + * if lost_node_addr is not null, all reference to it will be removed (aka lost node) + */ +static void _update_children(struct netaddr *last_addr, struct netaddr *lost_node_addr) +{ + DEBUGF("%s, %s", netaddr_to_str_s(&nbuf[0], last_addr), + netaddr_to_str_s(&nbuf[1], lost_node_addr)); + + struct olsr_node *node; + avl_for_each_element(get_olsr_head(), node, node) { + + if (lost_node_addr != NULL) { + remove_other_route(node, lost_node_addr); + + if (node->flood_mpr != NULL && netaddr_cmp(lost_node_addr, node->flood_mpr) == 0) { + node->flood_mpr = netaddr_free(node->flood_mpr); + } + } + + if (node->last_addr != NULL && netaddr_cmp(node->last_addr, last_addr) == 0) { + + if (lost_node_addr != NULL) { + remove_default_node(node); + } + else { + push_default_route(node); + } + + add_free_node(node); + + _update_children(node->addr, lost_node_addr); + } + } +} + +static void _olsr_node_expired(struct olsr_node *node) +{ + DEBUGF(); + + remove_default_node(node); + _update_children(node->addr, NULL); + + add_free_node(node); + + // 1-hop neighbors will become normal olsr_nodes here, should we care? (possible waste of memory) +} + +static void _remove_olsr_node(struct olsr_node *node) +{ + DEBUGF(); + + avl_remove(get_olsr_head(), &node->node); + remove_free_node(node); + + /* remove other routes from node that is about to be deleted */ + char skipped; + struct alt_route *route, *prev; + simple_list_for_each_safe(node->other_routes, route, prev, skipped) { + netaddr_free(route->last_addr); + simple_list_for_each_remove(&node->other_routes, route, prev); + } + + netaddr_free(node->flood_mpr); + + remove_default_node(node); + _update_children(node->addr, node->addr); + +#ifdef ENABLE_NAME + + if (node->name) { + free(node->name); + } + +#endif + netaddr_free(node->addr); + free(node); +} + +static bool _route_expired(struct olsr_node *node, struct netaddr *last_addr) +{ + if (node->last_addr != NULL && netaddr_cmp(node->last_addr, last_addr) == 0) { + return time_now() > node->expires; + } + + if (node->other_routes == NULL) { + return true; + } + + struct alt_route *route = simple_list_find_memcmp(node->other_routes, last_addr); + + if (route == NULL) { + return true; + } + + return time_now() > route->expires; +} + +static void _update_link_quality(struct nhdp_node *node) +{ + DEBUGF("%s", netaddr_to_str_s(&nbuf[0], h1_super(node)->addr)); + + if (_route_expired(h1_super(node), get_local_addr())) { + node->link_quality = node->link_quality * (1 - OLSR2_HYST_SCALING); + } + else { + node->link_quality = node->link_quality * (1 - OLSR2_HYST_SCALING) + OLSR2_HYST_SCALING; + } + + if (!h1_super(node)->pending && node->link_quality < OLSR2_HYST_LOW) { + h1_super(node)->pending = 1; + h1_super(node)->lost = LOST_ITER_MAX; + + if (node->mpr_neigh_flood > 0) { + _get_new_flood_mpr(h1_super(node)->addr); + } + + node->mpr_neigh_flood = 0; + node->mpr_neigh_route = 0; + + add_free_node(h1_super(node)); + push_default_route(h1_super(node)); + _update_children(h1_super(node)->addr, NULL); + } + + if (h1_super(node)->pending && node->link_quality > OLSR2_HYST_HIGH) { + h1_super(node)->pending = 0; + h1_super(node)->lost = 0; + + /* node may just have become a 1-hop node */ + push_default_route(h1_super(node)); + add_free_node(h1_super(node)); + } +} + +bool remove_expired(struct olsr_node *node) +{ + time_t _now = time_now(); + + if (node->type == NODE_TYPE_NHDP) { + _update_link_quality(h1_deriv(node)); + } + + char skipped; + struct alt_route *route, *prev; + simple_list_for_each_safe(node->other_routes, route, prev, skipped) { + if (_now - route->expires < OLSR2_HOLD_TIME_SECONDS) { + continue; + } + + DEBUG("alternative route to %s (%s) via %s expired, removing it", + node->name, netaddr_to_str_s(&nbuf[0], node->addr), + netaddr_to_str_s(&nbuf[1], route->last_addr)); + simple_list_for_each_remove(&node->other_routes, route, prev); + } + + if (_now - node->expires > OLSR2_HOLD_TIME_SECONDS) { + + DEBUG("%s (%s) expired", + node->name, netaddr_to_str_s(&nbuf[0], node->addr)); + + if (node->other_routes == NULL) { + _remove_olsr_node(node); + return true; + } + else { + _olsr_node_expired(node); + } + } + + return false; +} + +void route_expired(struct olsr_node *node, struct netaddr *last_addr) +{ + DEBUG("%s (%s) over %s expired", + node->name, netaddr_to_str_s(&nbuf[0], node->addr), + netaddr_to_str_s(&nbuf[1], last_addr)); + + if (node->last_addr != NULL && netaddr_cmp(node->last_addr, last_addr) == 0) { + _olsr_node_expired(node); + } + else { + remove_other_route(node, last_addr); + } + + if (node->last_addr == NULL && node->other_routes == NULL) { + _remove_olsr_node(node); + } +} + +void add_olsr_node(struct netaddr *addr, struct netaddr *last_addr, uint8_t vtime, uint8_t distance, metric_t metric, char *name) +{ + struct olsr_node *n = get_node(addr); + + if (n == NULL) { + n = _new_olsr_node(addr, distance, metric, vtime, name); + } + + if (n == NULL) { + puts("ERROR: add_olsr_node failed - out of memory"); + return; + } + + /* we have added a new node */ + if (n->last_addr == NULL) { +#ifdef ENABLE_NAME + + if (n->name == NULL && name != NULL) { + n->name = strdup(name); + } + +#endif + add_other_route(n, last_addr, distance, metric, vtime); + add_free_node(n); + + return; + } + + struct olsr_node *new_lh = get_node(last_addr); + + /* minimize MPR count */ + if (new_lh->type == NODE_TYPE_NHDP) { + + /* see if a better flooding MPR is availiable */ + if (n->flood_mpr != NULL && netaddr_cmp(n->flood_mpr, last_addr) != 0) { + struct nhdp_node *old_flood_mpr = h1_deriv(get_node(n->flood_mpr)); + + if (old_flood_mpr != NULL && h1_deriv(new_lh)->flood_neighbors > old_flood_mpr->flood_neighbors) { + DEBUG("switching flooding MPR (%s -> %s)", h1_super(old_flood_mpr)->name, new_lh->name); + old_flood_mpr->mpr_neigh_flood--; + h1_deriv(new_lh)->mpr_neigh_flood++; + netaddr_switch(&n->flood_mpr, last_addr); + } + } + + /* see if a better routing MPR is availiable */ + if (new_lh->path_metric + metric == n->path_metric && netaddr_cmp(last_addr, n->last_addr) != 0) { + struct nhdp_node *cur_mpr = h1_deriv(get_node(n->next_addr)); + + /* see if the new route is better, that means uses a neighbor that is alreay + used for reaching (more) 2-hop neighbors. */ + if (cur_mpr != NULL && (new_lh != NULL && + h1_deriv(new_lh)->mpr_neigh_route + 1 > cur_mpr->mpr_neigh_route)) { + DEBUG("switching routing MPR (%s -> %s)", h1_super(cur_mpr)->name, new_lh->name); + _update_children(n->addr, NULL); + push_default_route(n); + add_free_node(n); + } + } + } + + /* worse or same route */ + if (new_lh->path_metric + metric >= n->path_metric || netaddr_cmp(last_addr, n->last_addr) == 0) { + add_other_route(n, last_addr, distance, metric, vtime); + return; + } + + DEBUG("better route found (old: %d (%d) hops over %s new: %d (%d) hops over %s)", + n->distance, n->path_metric, netaddr_to_str_s(&nbuf[0], n->last_addr), + distance, new_lh->path_metric + metric, netaddr_to_str_s(&nbuf[1], last_addr)); + + n->distance = distance; // only to keep free_nodes sorted + _update_children(n->addr, NULL); + push_default_route(n); + add_other_route(n, last_addr, distance, metric, vtime); + add_free_node(n); +} + +bool is_known_msg(struct netaddr *addr, uint16_t seq_no, uint8_t vtime) +{ + struct olsr_node *node = get_node(addr); + + if (!node) { + node = _new_olsr_node(addr, 255, RFC5444_METRIC_INFINITE, vtime, NULL); + node->seq_no = seq_no; + return false; + } + + uint16_t tmp = node->seq_no; + + /* S1 > S2 AND S1 - S2 < MAXVALUE/2 OR + S2 > S1 AND S2 - S1 > MAXVALUE/2 */ + if (((seq_no > tmp) && (seq_no - tmp < (1 << 15))) || + ((seq_no < tmp) && (tmp - seq_no > (1 << 15)))) { + node->seq_no = seq_no; + return false; + } + + return true; +} + +#ifdef ENABLE_NAME +void print_routing_graph(void) +{ + puts("\n----BEGIN ROUTING GRAPH----\n"); + puts("subgraph routing {"); + puts("\tedge [ color = red ]"); + struct olsr_node *node, *tmp; + avl_for_each_element(get_olsr_head(), node, node) { + if (node->addr != NULL && node->last_addr != NULL) { + tmp = get_node(node->last_addr); + printf("\t%s -> %s\n", tmp ? tmp->name : olsr2_local_name, node->name); + } + } + puts("}"); + + puts("subgraph mpr_f {"); + puts("\tedge [ color = green ]"); + puts("// BEGIN FLOODING MPR"); + avl_for_each_element(get_olsr_head(), node, node) { + if (node->type == NODE_TYPE_NHDP && h1_deriv(node)->mpr_slctr_flood) { + printf("\t%s -> %s\n", node->name, olsr2_local_name); + } + } + puts("// END FLOODING MPR"); + puts("}"); + + puts("subgraph mpr_r {"); + puts("\tedge [ color = blue ]"); + puts("// BEGIN ROUTING MPR"); + avl_for_each_element(get_olsr_head(), node, node) { + if (node->distance == 1 && h1_deriv(node)->mpr_slctr_route) { + printf("\t%s -> %s\n", node->name, olsr2_local_name); + } + } + puts("// END ROUTING MPR"); + puts("}"); + + puts("\n----END ROUTING GRAPH----\n"); + +} +#else +void print_routing_graph(void) {} +#endif + +void print_topology_set(void) +{ +#ifndef ENABLE_DEBUG + struct netaddr_str nbuf[3]; +#endif + struct alt_route *route; + struct olsr_node *node; + + puts(""); + puts("---[ Topology Set ]--"); +#ifdef ENABLE_NAME + printf(" [ %s | %s ]\n", netaddr_to_str_s(&nbuf[0], get_local_addr()), olsr2_local_name); +#else + printf(" [%s]\n", netaddr_to_str_s(&nbuf[0], get_local_addr())); +#endif + + avl_for_each_element(get_olsr_head(), node, node) { + + printf("%s ", netaddr_to_str_s(&nbuf[0], node->addr)); +#ifdef ENABLE_NAME + printf("(%s)", node->name); +#endif + printf("\t=> %s; %d hops, metric: %u, next: %s (%u), %lds ", + netaddr_to_str_s(&nbuf[1], node->last_addr), + node->distance, + (unsigned int) node->path_metric, + netaddr_to_str_s(&nbuf[2], node->next_addr), + (unsigned int) node->link_metric, + node->expires - time_now()); + + if (node->type == NODE_TYPE_NHDP) { + printf("%s %.2f ", + node->pending ? "pending" : "", + h1_deriv(node)->link_quality); + printf("[%d/%d|%d] [%s%s]", + h1_deriv(node)->mpr_neigh_flood, + h1_deriv(node)->flood_neighbors, + h1_deriv(node)->mpr_neigh_route, + h1_deriv(node)->mpr_slctr_flood ? "F" : " ", + h1_deriv(node)->mpr_slctr_route ? "R" : " " + ); + } + + if (node->flood_mpr != NULL) { + printf(" flood: %s", netaddr_to_str_s(&nbuf[0], node->flood_mpr)); + } + + puts(""); + + simple_list_for_each(node->other_routes, route) { + printf("\t\t\t=> %s (%u); %ld s\n", + netaddr_to_str_s(&nbuf[0], route->last_addr), + (unsigned int) route->link_metric, + route->expires - time_now()); + } + } + puts("---------------------"); +} diff --git a/sys/net/routing/olsr2/olsr.h b/sys/net/routing/olsr2/olsr.h new file mode 100644 index 000000000000..bb331487252f --- /dev/null +++ b/sys/net/routing/olsr2/olsr.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @author Benjamin Valentin + * @} + */ + +#ifndef OLSR_H_ +#define OLSR_H_ + +#include + +#include "common/avl.h" +#include "common/netaddr.h" + +#include "node.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void add_olsr_node(struct netaddr *addr, struct netaddr *last_addr, uint8_t vtime, uint8_t distance, metric_t metric, char *name); +bool is_known_msg(struct netaddr *src, uint16_t seq_no, uint8_t vtime); +bool remove_expired(struct olsr_node *node); +void route_expired(struct olsr_node *node, struct netaddr *last_addr); + +void print_topology_set(void); +void print_routing_graph(void); + +#ifdef __cplusplus +} +#endif + +#endif /* OLSR_H_ */ diff --git a/sys/net/routing/olsr2/olsr_init.c b/sys/net/routing/olsr2/olsr_init.c new file mode 100644 index 000000000000..c8114ea278ac --- /dev/null +++ b/sys/net/routing/olsr2/olsr_init.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @author Benjamin Valentin + * @} + */ + +/*************************************************************** + * This file is for initialisation of the olsr2 module on RIOT * + ***************************************************************/ + +#ifdef RIOT +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rfc5444/rfc5444_writer.h" + +#include "constants.h" +#include "debug.h" +#include "node.h" +#include "olsr.h" +#include "reader.h" +#include "writer.h" + +#include + +#ifdef ENABLE_DEBUG +static struct netaddr_str nbuf[1]; +#endif + +static char receive_thread_stack[KERNEL_CONF_STACKSIZE_MAIN]; +static char sender_thread_stack[KERNEL_CONF_STACKSIZE_MAIN]; + +struct timer_msg { + vtimer_t timer; + timex_t interval; + void (*func)(void); +}; + +static struct timer_msg msg_hello = { .interval = { .seconds = OLSR2_HELLO_REFRESH_INTERVAL_SECONDS - 1, .microseconds = 0}, .func = writer_send_hello }; +static struct timer_msg msg_tc = { .interval = { .seconds = OLSR2_TC_REFRESH_INTERVAL_SECONDS - 1, .microseconds = 0}, .func = writer_send_tc }; + +static int sock; +static sockaddr6_t sa_bcast; +static mutex_t olsr_data; + +#if defined(BOARD_NATIVE) && defined(ENABLE_NAME) +static char _name[5]; +static char *gen_name(char *dest, const size_t len) +{ + for (unsigned int i = 0; i < len - 1; ++i) { + dest[i] = 'A' + (genrand_uint32() % ('Z' - 'A')); + } + + dest[len - 1] = '\0'; + return dest; +} +#endif + +static void write_packet(struct rfc5444_writer *wr __attribute__((unused)), + struct rfc5444_writer_target *iface __attribute__((unused)), + void *buffer, size_t length) +{ + +#ifdef ENABLE_LEDS + LED_GREEN_TOGGLE; +#endif +#ifdef ENABLE_DEBUG + int bytes_send = +#endif + socket_base_sendto(sock, buffer, length, 0, &sa_bcast, sizeof sa_bcast); + + DEBUG("write_packet(%d bytes), %d bytes sent", length, bytes_send); +} + +static void* olsr_receiver_thread(void *ctx __attribute__((unused))) +{ + char buffer[256]; + + sockaddr6_t sa = {0}; + sa.sin6_family = AF_INET6; + sa.sin6_port = HTONS(MANET_PORT); + + if (socket_base_bind(sock, &sa, sizeof sa) < 0) { + printf("Error bind failed!\n"); + socket_base_close(sock); + } + + int32_t recsize; + uint32_t fromlen = sizeof sa; + + struct netaddr _src; + _src._type = AF_INET6; + _src._prefix_len = 128; + + while (1) { + recsize = socket_base_recvfrom(sock, &buffer, sizeof buffer, 0, &sa, &fromlen); +#ifdef ENABLE_LEDS + LED_RED_TOGGLE; +#endif + memcpy(&_src._addr, &sa.sin6_addr, sizeof _src._addr); + DEBUG("received %d bytes from %s", recsize, netaddr_to_str_s(&nbuf[0], &_src)); + + mutex_lock(&olsr_data); + reader_handle_packet(&buffer, recsize, &_src, 1); // TODO: proper metric + mutex_unlock(&olsr_data); + } + + return NULL; +} + +static void* olsr_sender_thread(void *ctx __attribute__((unused))) +{ + DEBUG("olsr_sender_thread, pid %d\n", thread_getpid()); + + /* message queue, so messages don't get lost */ + msg_t msgq[2]; + msg_init_queue(msgq, sizeof msgq); + + while (1) { + msg_t m; + msg_receive(&m); + struct timer_msg *tmsg = (struct timer_msg *) m.content.ptr; + + mutex_lock(&olsr_data); + tmsg->func(); + mutex_unlock(&olsr_data); + + /* add jitter */ + tmsg->interval.microseconds = genrand_uint32() % OLSR2_MAX_JITTER_US; + + if (vtimer_set_msg(&tmsg->timer, tmsg->interval, thread_getpid(), tmsg) != 0) { + DEBUG("vtimer_set_msg failed, stopped sending"); + } + } + return NULL; +} + +static ipv6_addr_t *get_next_hop(ipv6_addr_t *dest) +{ + struct olsr_node *node = get_node((struct netaddr *) dest); // get_node will only look at the first few bytes + + if (node == NULL) { + return NULL; + } + + return (ipv6_addr_t *) node->next_addr; +} + +#ifdef ENABLE_NAME +ipv6_addr_t *get_ip_by_name(char *name) +{ + struct olsr_node *node; + avl_for_each_element(get_olsr_head(), node, node) { + if (node->name != NULL && strcmp(node->name, name) == 0) { + return (ipv6_addr_t *) node->addr; + } + } + + return NULL; +} +#endif + +void olsr_init(void) +{ + +#ifdef ENABLE_NAME +#ifdef BOARD_NATIVE + olsr2_local_name = gen_name(_name, sizeof _name); +#else + olsr2_local_name = sysconfig.name; +#endif +#endif + mutex_init(&olsr_data); + node_init(); + reader_init(); + writer_init(write_packet); + + /* we always send to the same broadcast address, prepare it once */ + sa_bcast.sin6_family = AF_INET6; + sa_bcast.sin6_port = HTONS(MANET_PORT); + ipv6_addr_set_all_nodes_addr(&sa_bcast.sin6_addr); + + /* enable receive */ + sock = socket_base_socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + thread_create(receive_thread_stack, sizeof receive_thread_stack, PRIORITY_MAIN - 1, CREATE_STACKTEST, olsr_receiver_thread, NULL, "olsr_rec"); + + /* set get_local_addr() */ + get_local_addr()->_type = AF_INET6; + get_local_addr()->_prefix_len = 128; + ipv6_net_if_get_best_src_addr((ipv6_addr_t *) get_local_addr(), &sa_bcast.sin6_addr); + + /* register olsr for routing */ + ipv6_iface_set_routing_provider(get_next_hop); + + DEBUG("This is node %s with IP %s", olsr2_local_name, netaddr_to_str_s(&nbuf[0], get_local_addr())); + + /* enable sending */ + int pid = thread_create(sender_thread_stack, sizeof sender_thread_stack, PRIORITY_MAIN - 1, CREATE_STACKTEST, olsr_sender_thread, NULL, "olsr_snd"); + + msg_t m; + DEBUG("setting up HELLO timer"); + m.content.ptr = (char *) &msg_hello; + msg_try_send(&m, pid); + + sleep_s(1); + DEBUG("setting up TC timer"); + m.content.ptr = (char *) &msg_tc; + msg_try_send(&m, pid); +} + +#endif /* RIOT */ diff --git a/sys/net/routing/olsr2/reader.c b/sys/net/routing/olsr2/reader.c new file mode 100644 index 000000000000..1533124a4b8e --- /dev/null +++ b/sys/net/routing/olsr2/reader.c @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @author Benjamin Valentin + * @} + */ + +#include +#include + +#include "common/common_types.h" +#include "common/netaddr.h" +#include "rfc5444/rfc5444.h" +#include "rfc5444/rfc5444_iana.h" +#include "rfc5444/rfc5444_reader.h" + +#ifdef ENABLE_DEBUG +static struct netaddr_str nbuf[1]; +#endif + +#ifdef RIOT +#include "net_help.h" +#endif + +#include "debug.h" +#include "nhdp.h" +#include "olsr.h" +#include "reader.h" +#include "writer.h" +#include "constants.h" +#include "routing_table.h" + +static struct rfc5444_reader reader; +static struct netaddr *current_src; +static struct olsr_node *current_node; // only set by _cb_nhdp_blocktlv_packet_okay + +/* ughh… these variables are needed in the addr callback, but read in the packet callback */ +static uint8_t vtime; +static uint8_t hops; +static uint16_t _seq_no; +static metric_t metric; + +static enum rfc5444_result _cb_nhdp_blocktlv_packet_okay(struct rfc5444_reader_tlvblock_context *cont); +static enum rfc5444_result _cb_nhdp_blocktlv_address_okay(struct rfc5444_reader_tlvblock_context *cont); + +static enum rfc5444_result _cb_olsr_blocktlv_packet_okay(struct rfc5444_reader_tlvblock_context *cont); +static enum rfc5444_result _cb_olsr_blocktlv_address_okay(struct rfc5444_reader_tlvblock_context *cont); + +static enum rfc5444_result _cb_packet_end(struct rfc5444_reader_tlvblock_context *cont, bool dropped); + +/* HELLO message */ +static struct rfc5444_reader_tlvblock_consumer_entry _nhdp_message_tlvs[] = { + [IDX_TLV_VTIME] = { .type = RFC5444_MSGTLV_VALIDITY_TIME, .mandatory = true }, +#ifdef ENABLE_NAME + [IDX_TLV_NODE_NAME] = { .type = RFC5444_TLV_NODE_NAME }, +#endif +}; + +static struct rfc5444_reader_tlvblock_consumer_entry _nhdp_address_tlvs[] = { + [IDX_ADDRTLV_MPR] = { .type = RFC5444_ADDRTLV_MPR }, + [IDX_ADDRTLV_LINK_STATUS] = { .type = RFC5444_ADDRTLV_LINK_STATUS }, + [IDX_ADDRTLV_METRIC] = { .type = RFC5444_ADDRTLV_LINK_METRIC}, +#ifdef ENABLE_NAME + [IDX_ADDRTLV_NODE_NAME] = { .type = RFC5444_TLV_NODE_NAME }, +#endif +}; + +/* TC message */ +static struct rfc5444_reader_tlvblock_consumer_entry _olsr_message_tlvs[] = { + [IDX_TLV_VTIME] = { .type = RFC5444_MSGTLV_VALIDITY_TIME, .mandatory = true }, +#ifdef ENABLE_NAME + [IDX_TLV_NODE_NAME] = { .type = RFC5444_TLV_NODE_NAME }, +#endif +}; + +static struct rfc5444_reader_tlvblock_consumer_entry _olsr_address_tlvs[] = { + [IDX_ADDRTLV_LINK_STATUS] = { .type = RFC5444_ADDRTLV_LINK_STATUS }, + [IDX_ADDRTLV_METRIC] = { .type = RFC5444_ADDRTLV_LINK_METRIC}, +#ifdef ENABLE_NAME + [IDX_ADDRTLV_NODE_NAME] = { .type = RFC5444_TLV_NODE_NAME }, +#endif +}; + +/* define callbacks for HELLO message */ +static struct rfc5444_reader_tlvblock_consumer _nhdp_consumer = { + .msg_id = RFC5444_MSGTYPE_HELLO, + .block_callback = _cb_nhdp_blocktlv_packet_okay, + .end_callback = _cb_packet_end, +}; + +static struct rfc5444_reader_tlvblock_consumer _nhdp_address_consumer = { + .msg_id = RFC5444_MSGTYPE_HELLO, + .addrblock_consumer = true, + .block_callback = _cb_nhdp_blocktlv_address_okay, +}; + +/* define callbacks for TC message */ +static struct rfc5444_reader_tlvblock_consumer _olsr_consumer = { + .msg_id = RFC5444_MSGTYPE_TC, + .block_callback = _cb_olsr_blocktlv_packet_okay, + .end_callback = _cb_packet_end, +}; + +static struct rfc5444_reader_tlvblock_consumer _olsr_address_consumer = { + .msg_id = RFC5444_MSGTYPE_TC, + .addrblock_consumer = true, + .block_callback = _cb_olsr_blocktlv_address_okay, +}; + +/* HELLO message */ +static enum rfc5444_result +_cb_nhdp_blocktlv_packet_okay(struct rfc5444_reader_tlvblock_context *cont __attribute__((unused))) +{ + DEBUG("received HELLO message:"); + + if (netaddr_cmp(get_local_addr(), current_src) == 0) { + return RFC5444_DROP_PACKET; + } + + /* VTIME is defined as mandatory */ + vtime = rfc5444_timetlv_decode(*_nhdp_message_tlvs[IDX_TLV_VTIME].tlv->single_value); + + char *name = NULL; +#ifdef ENABLE_NAME + + if (_nhdp_message_tlvs[IDX_TLV_NODE_NAME].tlv) { + name = (char *) _nhdp_message_tlvs[IDX_TLV_NODE_NAME].tlv->single_value; + DEBUG("\tfrom: %s (%s)", name, netaddr_to_str_s(&nbuf[0], current_src)); + } + +#endif + + DEBUG("\tmetric: %d", metric); + + current_node = olsr2_add_neighbor(current_src, metric, vtime, name); + + if (current_node == NULL) { + puts("ERROR: olsr2_add_neighbor failed - out of memory"); + return RFC5444_DROP_PACKET; + } + + /* reset MPR selector state, will be set by _cb_nhdp_blocktlv_address_okay */ + h1_deriv(current_node)->mpr_slctr_route = 0; + h1_deriv(current_node)->mpr_slctr_flood = 0; + + if (current_node->pending) { + return RFC5444_DROP_PACKET; + } + + return RFC5444_OKAY; +} + +/* HELLO announced addresses */ +static enum rfc5444_result +_cb_nhdp_blocktlv_address_okay(struct rfc5444_reader_tlvblock_context *cont) +{ + struct rfc5444_reader_tlvblock_entry *tlv; + metric_t link_metric = RFC5444_METRIC_MIN; + + char *name = NULL; +#ifdef ENABLE_NAME + + if ((tlv = _nhdp_address_tlvs[IDX_ADDRTLV_NODE_NAME].tlv)) { + name = (char *) tlv->single_value; + DEBUG("\t2-hop neighbor: %s (%s)", name, netaddr_to_str_s(&nbuf[0], &cont->addr)); + } + +#endif + + if ((tlv = _nhdp_address_tlvs[IDX_ADDRTLV_METRIC].tlv)) { + link_metric = rfc5444_metric_decode(*((uint16_t *) tlv->single_value)); + DEBUG("\t\tmetric: %d", link_metric); + } + + if ((tlv = _nhdp_address_tlvs[IDX_ADDRTLV_LINK_STATUS].tlv)) { + switch (* (char *) tlv->single_value) { + struct olsr_node *lost; + + case RFC5444_LINKSTATUS_LOST: + lost = get_node(&cont->addr); + DEBUG("\t\texpired node reported, removing it (HELLO)%s", lost ? "" : " [not found]"); + + if (lost != NULL) { + route_expired(lost, current_node->addr); + } + + return RFC5444_DROP_ADDRESS; + + default: + DEBUG("\t\tunknown LINKSTATUS = %d", * (char *) tlv->single_value); + } + } + + /* node broadcasts us as it's neighbor */ + if (netaddr_cmp(&cont->addr, get_local_addr()) == 0) { + + /* node selected us as mpr */ + if ((tlv = _nhdp_address_tlvs[IDX_ADDRTLV_MPR].tlv)) { + h1_deriv(current_node)->mpr_slctr_flood = (*tlv->single_value & RFC5444_MPR_FLOODING) > 0; + h1_deriv(current_node)->mpr_slctr_route = (*tlv->single_value & RFC5444_MPR_ROUTING) > 0; + + DEBUG("\tflood: %d, route: %d", h1_deriv(current_node)->mpr_slctr_flood, h1_deriv(current_node)->mpr_slctr_route); + } + + } + else { + add_olsr_node(&cont->addr, current_src, vtime, 2, link_metric, name); + } + + return RFC5444_OKAY; +} + +/* TC message */ +static enum rfc5444_result +_cb_olsr_blocktlv_packet_okay(struct rfc5444_reader_tlvblock_context *cont) +{ + DEBUG("received TC message:"); + + if (!cont->has_origaddr) { + return RFC5444_DROP_PACKET; + } + + if (!cont->has_seqno) { + return RFC5444_DROP_PACKET; + } + + if (!cont->has_hopcount || !cont->has_hoplimit) { + return RFC5444_DROP_PACKET; + } + + if (!netaddr_cmp(get_local_addr(), current_src)) { + return RFC5444_DROP_PACKET; + } + + if (!netaddr_cmp(get_local_addr(), &cont->orig_addr)) { + return RFC5444_DROP_PACKET; + } + + vtime = rfc5444_timetlv_decode(*_olsr_message_tlvs[IDX_TLV_VTIME].tlv->single_value); + + if (is_known_msg(&cont->orig_addr, cont->seqno, vtime)) { + return RFC5444_DROP_PACKET; + } + + DEBUG("\tfrom: %s", netaddr_to_str_s(&nbuf[0], &cont->orig_addr)); + DEBUG("\tsender: %s", netaddr_to_str_s(&nbuf[0], current_src)); + DEBUG("\tseqno: %d", cont->seqno); + DEBUG("\thops: %d", cont->hopcount); + + hops = cont->hopcount + 1; /* hopcount starts with 0 for A -> B */ + _seq_no = cont->seqno; + + return RFC5444_OKAY; +} + +/* TC announced addresses */ +static enum rfc5444_result +_cb_olsr_blocktlv_address_okay(struct rfc5444_reader_tlvblock_context *cont) +{ + struct rfc5444_reader_tlvblock_entry *tlv __attribute__((unused)); + metric_t link_metric = RFC5444_METRIC_MIN; + char *name = NULL; + + if (netaddr_cmp(get_local_addr(), &cont->addr) == 0) { + return RFC5444_DROP_ADDRESS; + } + +#ifdef ENABLE_NAME + + if ((tlv = _olsr_address_tlvs[IDX_ADDRTLV_NODE_NAME].tlv)) { + name = (char *) tlv->single_value; + DEBUG("\tannounces: %s (%s)", name, netaddr_to_str_s(&nbuf[0], &cont->addr)); + } + +#endif + + if ((tlv = _olsr_address_tlvs[IDX_ADDRTLV_LINK_STATUS].tlv)) { + switch (* (char *) tlv->single_value) { + struct olsr_node *lost; + + case RFC5444_LINKSTATUS_LOST: + lost = get_node(&cont->addr); + DEBUG("\texpired node reported, removing it (TC)%s", lost ? "" : " [not found]"); + + if (lost != NULL) { + route_expired(lost, &cont->orig_addr); + } + + /* emergency flood */ + struct nhdp_node *node = h1_deriv(get_node(current_src)); + + if (node != NULL) { + node->mpr_slctr_flood = 1; + } + + return RFC5444_DROP_ADDRESS; + + default: + DEBUG("\tunknown LINKSTATUS = %d", * (char *) tlv->single_value); + } + } + + if ((tlv = _olsr_address_tlvs[IDX_ADDRTLV_METRIC].tlv)) { + link_metric = rfc5444_metric_decode(*((uint16_t *) tlv->single_value)); + DEBUG("\t\tmetric: %d", link_metric); + } + + /* hops is hopcount to orig_addr, addr is one more hop */ + add_olsr_node(&cont->addr, &cont->orig_addr, vtime, hops + 1, link_metric, name); + + return RFC5444_OKAY; +} + +static enum rfc5444_result +_cb_packet_end(struct rfc5444_reader_tlvblock_context *cont __attribute__((unused)), bool dropped __attribute__((unused))) +{ + fill_routing_table(); + + return RFC5444_OKAY; +} + +/* this is only called for messages with hopcount/hoplimit, that is only TC messages */ +static void +_cb_olsr_forward_message(struct rfc5444_reader_tlvblock_context *context __attribute__((unused)), + uint8_t *buffer, size_t length) +{ + struct olsr_node *node = get_node(current_src); + + /* only forward if node selected us as flooding MPR */ + if (node == NULL || h1_deriv(node)->mpr_slctr_flood == 0) { + return; + } + + if (RFC5444_OKAY == rfc5444_writer_forward_msg(&writer, buffer, length)) { + DEBUG("\tforwarding"); + rfc5444_writer_flush(&writer, &interface, true); + } + else { + DEBUG("\tfailed forwarding package"); + } +} + +/** + * Initialize RFC5444 reader + */ +void reader_init(void) +{ + /* initialize reader */ + rfc5444_reader_init(&reader); + reader.forward_message = _cb_olsr_forward_message; + + /* register HELLO message consumer */ + rfc5444_reader_add_message_consumer(&reader, &_nhdp_consumer, _nhdp_message_tlvs, ARRAYSIZE(_nhdp_message_tlvs)); + rfc5444_reader_add_message_consumer(&reader, &_nhdp_address_consumer, _nhdp_address_tlvs, ARRAYSIZE(_nhdp_address_tlvs)); + + /* register TC message consumer */ + rfc5444_reader_add_message_consumer(&reader, &_olsr_consumer, _olsr_message_tlvs, ARRAYSIZE(_olsr_message_tlvs)); + rfc5444_reader_add_message_consumer(&reader, &_olsr_address_consumer, _olsr_address_tlvs, ARRAYSIZE(_olsr_address_tlvs)); +} + +/** + * Inject a package into the RFC5444 reader + */ +int reader_handle_packet(void *buffer, size_t length, struct netaddr *src, uint8_t metric_in) +{ + current_src = src; + metric = metric_in; + return rfc5444_reader_handle_packet(&reader, buffer, length); +} + +/** + * Cleanup RFC5444 reader + */ +void reader_cleanup(void) +{ + rfc5444_reader_cleanup(&reader); +} diff --git a/sys/net/routing/olsr2/reader.h b/sys/net/routing/olsr2/reader.h new file mode 100644 index 000000000000..0bde07fdaf70 --- /dev/null +++ b/sys/net/routing/olsr2/reader.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @author Benjamin Valentin + * @} + */ + +#ifndef OLSR2_READER_H_ +#define OLSR2_READER_H_ + +#include "common/common_types.h" +#include "rfc5444/rfc5444_reader.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void reader_init(void); +int reader_handle_packet(void *buffer, size_t length, struct netaddr *src, uint8_t metric_in); +void reader_cleanup(void); + +#ifdef __cplusplus +} +#endif + +#endif /* OLSR2_READER_H_ */ diff --git a/sys/net/routing/olsr2/routing_table.c b/sys/net/routing/olsr2/routing_table.c new file mode 100644 index 000000000000..e7d632d68e96 --- /dev/null +++ b/sys/net/routing/olsr2/routing_table.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @author Benjamin Valentin + * @} + */ + +#include +#include + +#ifdef ENABLE_DEBUG +static struct netaddr_str nbuf[3]; +#endif + +#include "olsr.h" +#include +#include "debug.h" +#include "util.h" +#include "routing_table.h" +#include "constants.h" +#include "rfc5444/rfc5444.h" + +/* sorted list, only for faster access + * Keeps yet unroutable nodes, so we don't have to traverse the entire list + */ +struct free_node { + struct free_node *next; + struct olsr_node *node; + uint8_t hops; // for sorting only +}; + +static struct free_node *_pending_head = 0; +static bool _update_pending = false; + +void add_free_node(struct olsr_node *node) +{ + struct free_node *n = simple_list_find_cmp(_pending_head, node, (int ( *)(void *, void *)) olsr_node_cmp); + + if (n == NULL) { + uint8_t hops = node->distance; + n = simple_list_add_before(&_pending_head, hops); + } + + if (n == NULL) { + printf("ERROR: out of memory in %s\n", __FUNCTION__); + return; + } + + n->node = node; + + node->next_addr = netaddr_free(node->next_addr); /* empty next_addr marks route as pending */ + _update_pending = true; +} + +bool remove_free_node(struct olsr_node *node) +{ + struct free_node *n = simple_list_find_cmp(_pending_head, node, (int ( *)(void *, void *)) olsr_node_cmp); + + if (n == NULL) { + return false; + } + + return simple_list_remove(&_pending_head, n); +} + +void fill_routing_table(void) +{ + struct free_node *head = _pending_head; + + if (_pending_head == NULL || !_update_pending) { + return; + } + + _update_pending = false; + DEBUG("update routing table"); + + struct free_node *fn; + bool noop = false; /* when in an iteration there was nothing removed from free nodes */ + + while (head != NULL && !noop) { + noop = true; /* if no nodes could be removed in an iteration, abort */ + struct free_node *prev; + char skipped; + simple_list_for_each_safe(head, fn, prev, skipped) { + DEBUG("trying to find a route to %s", fn->node->name); + /* chose shortest route from the set of availiable routes */ + metric_t min_mtrc = RFC5444_METRIC_INFINITE; + struct olsr_node *node = NULL; /* chosen route */ + struct nhdp_node *flood_mpr = NULL; + struct alt_route *route; /* current other_route */ + simple_list_for_each(fn->node->other_routes, route) { + DEBUG("\tconsidering %s (%d)", netaddr_to_string(&nbuf[0], route->last_addr), route->link_metric); + + /* the node is actually a neighbor of ours */ + if (netaddr_cmp(route->last_addr, get_local_addr()) == 0) { + DEBUG("\t\t1-hop neighbor"); + + /* don't use pending nodes */ + if (fn->node->pending) { + DEBUG("\t\tpending -> skipped"); + continue; + } + + if (route->link_metric > min_mtrc) { + continue; + } + + min_mtrc = route->link_metric; + node = fn->node; + continue; + } + + /* see if we can find a better route */ + struct olsr_node *_tmp = get_node(route->last_addr); + + if (_tmp == NULL || _tmp->addr == NULL || _tmp->next_addr == NULL) { + DEBUG("\t\tnot routable"); + continue; + } + + /* ignore pending nodes */ + if (_tmp->distance == 1 && _tmp->pending) { + DEBUG("\t\tpending -> skipped"); + continue; + } + + /* flooding MPR selection */ + if (_tmp->type == NODE_TYPE_NHDP && + (flood_mpr == NULL || flood_mpr->flood_neighbors < h1_deriv(_tmp)->flood_neighbors)) { + flood_mpr = h1_deriv(_tmp); + } + + if (_tmp->path_metric + route->link_metric > min_mtrc) { + DEBUG("\t\tdoesn't offer a better route, %d + %d > %d", _tmp->path_metric, route->link_metric, min_mtrc); + continue; + } + + /* try to minimize MPR count */ + if (_tmp->type == NODE_TYPE_NHDP && min_mtrc == _tmp->path_metric + route->link_metric) { + DEBUG("\t\tequaly good route found, try to optimize MPR seleciton"); + struct nhdp_node *old_mpr = h1_deriv(node); + + /* a direct neighbor might be reached over an additional hop, the true MPR */ + if (netaddr_cmp(_tmp->next_addr, _tmp->addr) != 0) { + old_mpr = h1_deriv(get_node(_tmp->next_addr)); + } + + /* use the neighbor with the most 2-hop neighbors */ + if (old_mpr->mpr_neigh_route >= h1_deriv(_tmp)->mpr_neigh_route + 1) { + DEBUG("\t\told MPR (%d) is better (new: %d)", old_mpr->mpr_neigh_route, h1_deriv(_tmp)->mpr_neigh_route); + continue; + } + } + + DEBUG("\t\t[possible candidate]"); + node = _tmp; + min_mtrc = _tmp->path_metric + route->link_metric; + } /* for each other_route */ + + if (flood_mpr != NULL) { + netaddr_switch(&fn->node->flood_mpr, h1_super(flood_mpr)->addr); + DEBUG("[%s] setting flood MPR to %s", __FUNCTION__, netaddr_to_str_s(&nbuf[0], fn->node->flood_mpr)); + flood_mpr->mpr_neigh_flood++; + } + else { + fn->node->flood_mpr = netaddr_free(fn->node->flood_mpr); + } + + /* We found a valid route */ + if (node == fn->node) { + DEBUG("\t%s (%s) is a 1-hop neighbor", + netaddr_to_str_s(&nbuf[0], fn->node->addr), fn->node->name); + noop = false; + fn->node->next_addr = netaddr_use(fn->node->addr); + fn->node->path_metric = fn->node->link_metric; + fn->node->distance = 1; + fn->node->lost = 0; + + pop_other_route(fn->node, get_local_addr()); + simple_list_for_each_remove(&head, fn, prev); + + } + else if (node != NULL) { + DEBUG("\t%s (%s) -> %s (%s) -> […] -> %s", + netaddr_to_str_s(&nbuf[0], fn->node->addr), fn->node->name, + netaddr_to_str_s(&nbuf[1], node->addr), node->name, + netaddr_to_str_s(&nbuf[2], node->next_addr)); + DEBUG("%d = %d", fn->node->distance, node->distance + 1); + + noop = false; + + /* update routing MPR information */ + if (node->type == NODE_TYPE_NHDP || fn->node->flood_mpr != NULL) { + struct nhdp_node *mpr = h1_deriv(get_node(node->next_addr)); + DEBUG("\tincrementing mpr_neigh_route for %s", h1_super(mpr)->name); + mpr->mpr_neigh_route++; + } + + fn->node->distance = node->distance + 1; + fn->node->path_metric = min_mtrc; + fn->node->next_addr = netaddr_use(node->next_addr); + + pop_other_route(fn->node, node->addr); + simple_list_for_each_remove(&head, fn, prev); + } + else { + DEBUG("\tdon't yet know how to route %s", netaddr_to_str_s(&nbuf[0], fn->node->addr)); + } + } + } + + _pending_head = head; + +#ifdef DEBUG + + while (head != NULL) { + DEBUG("Could not find next hop for %s (%s), should be %s (%d hops)", + netaddr_to_str_s(&nbuf[0], head->node->addr), head->node->name, + netaddr_to_str_s(&nbuf[1], head->node->last_addr), head->node->distance); + + head = head->next; + } + +#endif +} diff --git a/sys/net/routing/olsr2/routing_table.h b/sys/net/routing/olsr2/routing_table.h new file mode 100644 index 000000000000..593af2dbc480 --- /dev/null +++ b/sys/net/routing/olsr2/routing_table.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @author Benjamin Valentin + * @} + */ + +#ifndef ROUTING_H_ +#define ROUTING_H_ + +#include "node.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * add a node to the list of pending nodes + */ +void add_free_node(struct olsr_node *node); + +/* + * remove a node from the list of pending nodes + * returns true if node was found and removed + */ +bool remove_free_node(struct olsr_node *node); + +/* + * try to find a route for pending nodes + */ +void fill_routing_table(void); + +#ifdef __cplusplus +} +#endif + +#endif /* ROUTING_H_ */ diff --git a/sys/net/routing/olsr2/util.c b/sys/net/routing/olsr2/util.c new file mode 100644 index 000000000000..5eb63a2ec3b5 --- /dev/null +++ b/sys/net/routing/olsr2/util.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @author Benjamin Valentin + * @} + */ + +#include + +#ifdef RIOT +#include "vtimer.h" +#include "rtc.h" +#else +#include +#include +#endif + +#ifdef ENABLE_DEBUG +static struct netaddr_str nbuf[1]; +#endif + +#include "constants.h" +#include "util.h" +#include "node.h" +#include "debug.h" + +const char *netaddr_to_str_s(struct netaddr_str *dst, const struct netaddr *src) +{ + return src ? netaddr_to_string(dst, src) : NULL; +} + +struct netaddr *netaddr_dup(struct netaddr *addr) +{ + struct netaddr_rc *addr_new = calloc(1, sizeof(struct netaddr_rc)); + + if (addr_new == NULL) { + return NULL; + } + + addr_new->_refs = 1; + return memcpy(addr_new, addr, sizeof(struct netaddr)); +} + +struct netaddr *netaddr_use(struct netaddr *addr) +{ + ((struct netaddr_rc *) addr)->_refs++; + return addr; +} + +struct netaddr *netaddr_reuse(struct netaddr *addr) +{ + if (netaddr_cmp(addr, get_local_addr()) == 0) { + return netaddr_use(get_local_addr()); + } + + struct olsr_node *n = get_node(addr); + + if (!n) { + DEBUG("Address %s not found, this shouldn't happen", netaddr_to_str_s(&nbuf[0], addr)); + return netaddr_dup(addr); + } + + return netaddr_use(n->addr); +} + +struct netaddr *netaddr_free(struct netaddr *addr) +{ + struct netaddr_rc *addr_rc = (struct netaddr_rc *) addr; + + if (addr) { + DEBUG("netaddr_free(%s) - %d refs", netaddr_to_str_s(&nbuf[0], addr), addr_rc->_refs); + } + + if (addr != NULL && --addr_rc->_refs == 0) { + free(addr_rc); + } + + return NULL; +} + +void netaddr_switch(struct netaddr **old_addr, struct netaddr *new_addr) +{ + netaddr_free(*old_addr); + *old_addr = netaddr_reuse(new_addr); +} + +time_t time_now(void) +{ +#ifdef RIOT + struct timeval _tv; + return rtc_time(&_tv); +#else + return time(0); +#endif +} + +void sleep_s(int secs) +{ +#ifdef RIOT + vtimer_usleep(secs * SECOND); +#else + // process wakes up when a package arrives + // go back to sleep to prevent flooding + int remaining_sleep = secs; + + while ((remaining_sleep = sleep(remaining_sleep))); + +#endif +} diff --git a/sys/net/routing/olsr2/util.h b/sys/net/routing/olsr2/util.h new file mode 100644 index 000000000000..fc6acef6a202 --- /dev/null +++ b/sys/net/routing/olsr2/util.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @author Benjamin Valentin + * @} + */ + +#ifndef OLSR2_UTIL_H_ +#define OLSR2_UTIL_H_ + +#include "common/netaddr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct netaddr_rc { + struct netaddr super; + uint8_t _refs; +}; + +const char *netaddr_to_str_s(struct netaddr_str *dst, const struct netaddr *src); + +struct netaddr *netaddr_dup(struct netaddr *addr); +struct netaddr *netaddr_use(struct netaddr *addr); +struct netaddr *netaddr_reuse(struct netaddr *addr); +struct netaddr *netaddr_free(struct netaddr *addr); +void netaddr_switch(struct netaddr **old_addr, struct netaddr *new_addr); + +time_t time_now(void); +void sleep_s(int secs); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sys/net/routing/olsr2/writer.c b/sys/net/routing/olsr2/writer.c new file mode 100644 index 000000000000..750392d32704 --- /dev/null +++ b/sys/net/routing/olsr2/writer.c @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @author Benjamin Valentin + * @} + */ + +#ifdef RIOT +#include "net_help.h" +#endif + +#ifdef ENABLE_NAME +#include +#endif + +#include "common/avl.h" +#include "common/common_types.h" +#include "common/netaddr.h" +#include "rfc5444/rfc5444.h" +#include "rfc5444/rfc5444_iana.h" +#include "rfc5444/rfc5444_writer.h" + +#include "constants.h" +#include "writer.h" +#include "nhdp.h" +#include "olsr.h" +#include "debug.h" +#include "routing_table.h" + +uint8_t msg_buffer[256]; +uint8_t msg_addrtlvs[512]; +uint8_t packet_buffer[256]; + +uint16_t seq_no = 1; + +static bool send_tc_messages; + +static void _cb_add_nhdp_message_TLVs(struct rfc5444_writer *wr); +static void _cb_add_nhdp_addresses(struct rfc5444_writer *wr); + +static void _cb_add_olsr_message_TLVs(struct rfc5444_writer *wr); +static void _cb_add_olsr_addresses(struct rfc5444_writer *wr); + +/* HELLO message */ +static struct rfc5444_writer_content_provider _nhdp_message_content_provider = { + .msg_type = RFC5444_MSGTYPE_HELLO, + .addMessageTLVs = _cb_add_nhdp_message_TLVs, + .addAddresses = _cb_add_nhdp_addresses, +}; + +static struct rfc5444_writer_tlvtype _nhdp_addrtlvs[] = { + [IDX_ADDRTLV_MPR] = { .type = RFC5444_ADDRTLV_MPR }, + [IDX_ADDRTLV_LINK_STATUS] = { .type = RFC5444_ADDRTLV_LINK_STATUS }, + [IDX_ADDRTLV_METRIC] = { .type = RFC5444_ADDRTLV_LINK_METRIC}, +#ifdef ENABLE_NAME + [IDX_ADDRTLV_NODE_NAME] = { .type = RFC5444_TLV_NODE_NAME }, +#endif +}; + +/* TC message */ +static struct rfc5444_writer_content_provider _olsr_message_content_provider = { + .msg_type = RFC5444_MSGTYPE_TC, + .addMessageTLVs = _cb_add_olsr_message_TLVs, + .addAddresses = _cb_add_olsr_addresses, +}; + +static struct rfc5444_writer_tlvtype _olsr_addrtlvs[] = { + [IDX_ADDRTLV_LINK_STATUS] = { .type = RFC5444_ADDRTLV_LINK_STATUS }, + [IDX_ADDRTLV_METRIC] = { .type = RFC5444_ADDRTLV_LINK_METRIC}, +#ifdef ENABLE_NAME + [IDX_ADDRTLV_NODE_NAME] = { .type = RFC5444_TLV_NODE_NAME }, +#endif +}; + +/* add TLVs to HELLO message */ +static void +_cb_add_nhdp_message_TLVs(struct rfc5444_writer *wr) +{ + uint8_t time_encoded = rfc5444_timetlv_encode(OLSR2_HELLO_REFRESH_INTERVAL_SECONDS); + rfc5444_writer_add_messagetlv(wr, RFC5444_MSGTLV_VALIDITY_TIME, 0, &time_encoded, sizeof(time_encoded)); + +#ifdef ENABLE_NAME + rfc5444_writer_add_messagetlv(wr, RFC5444_TLV_NODE_NAME, 0, olsr2_local_name, strlen(olsr2_local_name) + 1); +#endif +} + +/* add addresses to HELLO message */ +static void +_cb_add_nhdp_addresses(struct rfc5444_writer *wr) +{ + struct olsr_node *node, *safe; + int value; + uint8_t mpr; + send_tc_messages = false; + + /* add all neighbors */ + avl_for_each_element_safe(get_olsr_head(), node, node, safe) { + + /* if the node was just removed entirely from the database, continue */ + if (remove_expired(node)) { + continue; + } + + if (node->type != NODE_TYPE_NHDP && !node->lost) { + continue; + } + + if (node->pending && !node->lost) { + continue; + } + + if (!node->pending && h1_deriv(node)->mpr_slctr_route) { + send_tc_messages = true; + } + + struct rfc5444_writer_address *address = rfc5444_writer_add_address(wr, + _nhdp_message_content_provider.creator, node->addr, false); + + mpr = 0; + + if (h1_deriv(node)->mpr_neigh_flood > 0) { + mpr |= RFC5444_MPR_FLOODING; + } + + if (h1_deriv(node)->mpr_neigh_route > 0) { + mpr |= RFC5444_MPR_ROUTING; + } + + if (mpr > 0) + rfc5444_writer_add_addrtlv(wr, address, &_nhdp_addrtlvs[IDX_ADDRTLV_MPR], + &mpr, sizeof mpr, false); + + metric_t link_metric = get_link_metric(node, get_local_addr()); + + if (link_metric > RFC5444_METRIC_MIN) { + uint16_t mtrc = rfc5444_metric_encode(link_metric); + rfc5444_writer_add_addrtlv(wr, address, &_nhdp_addrtlvs[IDX_ADDRTLV_METRIC], + &mtrc, sizeof mtrc, false); + } + + if (node->lost) { + DEBUG("LINKSTATUS: neighbor %s lost (HELLO) [%d]", node->name, node->lost); + value = RFC5444_LINKSTATUS_LOST; + rfc5444_writer_add_addrtlv(wr, address, &_nhdp_addrtlvs[IDX_ADDRTLV_LINK_STATUS], + &value, sizeof value, false); + + if (!send_tc_messages) { + node->lost--; + } + } + +#ifdef ENABLE_NAME + + if (node->name) + rfc5444_writer_add_addrtlv(wr, address, &_nhdp_addrtlvs[IDX_ADDRTLV_NODE_NAME], + node->name, strlen(node->name) + 1, false); + +#endif + } +} + +/* add TLVs to TC message */ +static void +_cb_add_olsr_message_TLVs(struct rfc5444_writer *wr) +{ + uint8_t time_encoded = rfc5444_timetlv_encode(OLSR2_TC_REFRESH_INTERVAL_SECONDS); + rfc5444_writer_add_messagetlv(wr, RFC5444_MSGTLV_VALIDITY_TIME, 0, &time_encoded, sizeof(time_encoded)); + +#ifdef ENABLE_NAME + rfc5444_writer_add_messagetlv(wr, RFC5444_TLV_NODE_NAME, 0, olsr2_local_name, strlen(olsr2_local_name) + 1); +#endif +} + +/* add addresses to TC message */ +static void +_cb_add_olsr_addresses(struct rfc5444_writer *wr) +{ + struct olsr_node *node; + int value; + + /* add all neighbors */ + avl_for_each_element(get_olsr_head(), node, node) { + if (h1_deriv(node) == NULL) { + continue; + } + + if (!h1_deriv(node)->mpr_slctr_route) { + continue; + } + + /* don't advertise neighbors routed over another hop */ + if (node->distance != 1 && !node->lost) { + continue; + } + + if (node->pending && !node->lost) { + continue; + } + + struct rfc5444_writer_address *address __attribute__((unused)); + + address = rfc5444_writer_add_address(wr, _olsr_message_content_provider.creator, node->addr, false); + + if (node->link_metric > RFC5444_METRIC_MIN) { + uint16_t mtrc = rfc5444_metric_encode(node->link_metric); + rfc5444_writer_add_addrtlv(wr, address, &_nhdp_addrtlvs[IDX_ADDRTLV_METRIC], + &mtrc, sizeof mtrc, false); + } + + if (node->lost) { + DEBUG("LINKSTATUS: neighbor %s lost (TC) [%d]", node->name, node->lost); + value = RFC5444_LINKSTATUS_LOST; + rfc5444_writer_add_addrtlv(wr, address, &_olsr_addrtlvs[IDX_ADDRTLV_LINK_STATUS], + &value, sizeof value, false); + + node->lost--; + } + +#ifdef ENABLE_NAME + + if (node->name) + rfc5444_writer_add_addrtlv(wr, address, &_olsr_addrtlvs[IDX_ADDRTLV_NODE_NAME], + node->name, strlen(node->name) + 1, false); + +#endif + } +} + +/* header for HELLO messages */ +static void +_cb_add_hello_message_header(struct rfc5444_writer *wr, struct rfc5444_writer_message *message) +{ + /* no originator, no hopcount, no hoplimit, no sequence number */ + rfc5444_writer_set_msg_header(wr, message, false, false, false, false); +} + +/* header for TC messages */ +static void +_cb_add_tc_message_header(struct rfc5444_writer *wr, struct rfc5444_writer_message *message) +{ + /* originator, hopcount, hoplimit, sequence number */ + rfc5444_writer_set_msg_header(wr, message, true, true, true, true); + rfc5444_writer_set_msg_seqno(wr, message, seq_no++); + rfc5444_writer_set_msg_originator(wr, message, netaddr_get_binptr(get_local_addr())); + + message->hoplimit = OLSR2_TC_HOP_LIMIT; +} + +/* reader has already decided whether to forward or not, just say ok to that */ +bool +olsr_message_forwarding_selector(struct rfc5444_writer_target *rfc5444_target __attribute__((unused))) +{ + return true; +} + +/** + * Initialize RFC5444 writer + * @param ptr pointer to "send_packet" function + */ +void +writer_init(write_packet_func_ptr ptr) +{ + struct rfc5444_writer_message *_hello_msg; + struct rfc5444_writer_message *_tc_msg; + + writer.msg_buffer = msg_buffer; + writer.msg_size = sizeof(msg_buffer); + writer.addrtlv_buffer = msg_addrtlvs; + writer.addrtlv_size = sizeof(msg_addrtlvs); + + interface.packet_buffer = packet_buffer; + interface.packet_size = sizeof(packet_buffer); + interface.sendPacket = ptr; + + /* initialize writer */ + rfc5444_writer_init(&writer); + + /* register a target (for sending messages to) in writer */ + rfc5444_writer_register_target(&writer, &interface); + + /* register a message content provider */ + rfc5444_writer_register_msgcontentprovider(&writer, &_nhdp_message_content_provider, _nhdp_addrtlvs, ARRAYSIZE(_nhdp_addrtlvs)); + rfc5444_writer_register_msgcontentprovider(&writer, &_olsr_message_content_provider, _olsr_addrtlvs, ARRAYSIZE(_olsr_addrtlvs)); + + /* register message type 1 with 16 byte addresses */ + _hello_msg = rfc5444_writer_register_message(&writer, RFC5444_MSGTYPE_HELLO, false, RFC5444_MAX_ADDRLEN); + _tc_msg = rfc5444_writer_register_message(&writer, RFC5444_MSGTYPE_TC, false, RFC5444_MAX_ADDRLEN); + + _hello_msg->addMessageHeader = _cb_add_hello_message_header; + _tc_msg->addMessageHeader = _cb_add_tc_message_header; + _tc_msg->forward_target_selector = olsr_message_forwarding_selector; +} + +void writer_send_hello(void) +{ + DEBUG("[HELLO]"); + + /* send message */ + rfc5444_writer_create_message_alltarget(&writer, RFC5444_MSGTYPE_HELLO); + rfc5444_writer_flush(&writer, &interface, false); +} + +void writer_send_tc(void) +{ + if (!send_tc_messages) { + return; + } + + DEBUG("[TC]"); + + /* send message */ + rfc5444_writer_create_message_alltarget(&writer, RFC5444_MSGTYPE_TC); + rfc5444_writer_flush(&writer, &interface, false); +} + +/** + * Cleanup RFC5444 writer + */ +void +writer_cleanup(void) +{ + rfc5444_writer_cleanup(&writer); +} diff --git a/sys/net/routing/olsr2/writer.h b/sys/net/routing/olsr2/writer.h new file mode 100644 index 000000000000..589a2d560bf2 --- /dev/null +++ b/sys/net/routing/olsr2/writer.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + * + * @ingroup olsr2 + * @{ + * @author Benjamin Valentin + * @} + */ + +#ifndef OLSR2_WRITER_H_ +#define OLSR2_WRITER_H_ + +#include "common/common_types.h" +#include "rfc5444/rfc5444_writer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*write_packet_func_ptr)( + struct rfc5444_writer *wr, struct rfc5444_writer_target *iface, void *buffer, size_t length); + +/* these are also used in reader.c, just acces them directly instead of passing a pointer */ +struct rfc5444_writer writer; +struct rfc5444_writer_target interface; + +void writer_init(write_packet_func_ptr ptr); +void writer_send_hello(void); +void writer_send_tc(void); +void writer_cleanup(void); + +#ifdef __cplusplus +} +#endif + +#endif /* OLSR2_WRITER_H_ */ diff --git a/sys/slist/Makefile b/sys/slist/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/sys/slist/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/slist/slist.c b/sys/slist/slist.c new file mode 100644 index 000000000000..f4ce8817eb20 --- /dev/null +++ b/sys/slist/slist.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** +* @file slist.c +* @author Benjamin Valentin +*/ + +#include +#include + +#include "slist.h" + +struct simple_list_elem { + struct simple_list_elem *next; +}; + +void *__simple_list_add_tail(struct simple_list_elem **head, void *mem) +{ + struct simple_list_elem *_head = *head; + + /* check out-of-memory condition */ + if (mem == NULL) { + return NULL; + } + + if (_head == NULL) { + *head = mem; + return *head; + } + + while (_head->next != NULL) { + _head = _head->next; + } + + _head = _head->next = mem; + return _head; +} + +void *__simple_list_add_head(struct simple_list_elem **head, void *mem) +{ + struct simple_list_elem *_head = *head; + + /* check out-of-memory condition */ + if (mem == NULL) { + return NULL; + } + + *head = mem; + (*head)->next = _head; + + return *head; +} + +void *__simple_list_add_before(struct simple_list_elem **head, void *mem, int needle, size_t offset) +{ + struct simple_list_elem *_head = *head; + struct simple_list_elem *prev = 0; + + /* check out-of-memory condition */ + if (mem == NULL) { + return NULL; + } + + if (_head == NULL) { + *head = mem; + return *head; + } + + while (_head != NULL) { + int *buff = (void *) ((char *) _head + offset); + + if (*buff > needle) { + if (prev != NULL) { + prev->next = mem; + prev->next->next = _head; + return prev->next; + } + + prev = mem; + prev->next = _head; + *head = prev; + return prev; + } + + prev = _head; + _head = _head->next; + } + + _head = prev->next = mem; + return _head; +} + +void *__simple_list_find(struct simple_list_elem *head, void *needle, size_t offset, size_t size) +{ + while (head != NULL) { + void **buff = (void *) ((char *) head + offset); + + if (size == 0 && *buff == needle) { + return head; + } + + if (size > 0 && memcmp(*buff, needle, size) == 0) { + return head; + } + + head = head->next; + } + + return 0; +} + +void *__simple_list_find_cmp(struct simple_list_elem *head, void *needle, size_t offset, int compare(void *, void *)) +{ + while (head != NULL) { + void **buff = (void *) ((char *) head + offset); + + if (compare(*buff, needle) == 0) { + return head; + } + + head = head->next; + } + + return 0; +} + +void *__simple_list_remove(struct simple_list_elem **head, struct simple_list_elem *node, int keep) +{ + struct simple_list_elem *_head = *head; + struct simple_list_elem *prev = 0; + + while (_head != NULL && _head != node) { + prev = _head; + _head = _head->next; + } + + /* not found */ + if (_head != node) { + return NULL; + } + + /* remove head */ + if (prev == NULL) { + *head = _head->next; + } + else { + prev->next = node->next; + } + + if (keep) { + return node; + } + + free(node); + return (void *) 1; +} + +void __simple_list_clear(struct simple_list_elem **head) +{ + struct simple_list_elem *tmp, *_head = *head; + + while (_head != NULL) { + tmp = _head; + _head = _head->next; + free(tmp); + } + + *head = NULL; +} diff --git a/tests/slist/Makefile b/tests/slist/Makefile new file mode 100644 index 000000000000..c531bcb32c6b --- /dev/null +++ b/tests/slist/Makefile @@ -0,0 +1,6 @@ +export APPLICATION = slist +include ../Makefile.tests_common + +USEMODULE += slist + +include $(RIOTBASE)/Makefile.include diff --git a/tests/slist/main.c b/tests/slist/main.c new file mode 100644 index 000000000000..3a806eb02675 --- /dev/null +++ b/tests/slist/main.c @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2014 Benjamin Valentin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief slist (simple list) test application + * + * @author Benjamin Valentin + * + * @} + */ + +#include +#include +#include +#include "slist.h" + +int fail = 0; +void cunit_named_check(int cond, const char *name, int line, const char *format, ...) +{ + va_list ap; + + if (cond) { + return; + } + + fail++; + + va_start(ap, format); + + printf("\t%s (%d) fail: ", name, line); + vprintf(format, ap); + puts("\n"); + va_end(ap); +} +#define CHECK_TRUE(cond, format, args...) cunit_named_check(cond, __func__, __LINE__, format, ##args); + +char foo[] = "Hello World!"; +char bar[] = "Hello CUnit!"; +char baz[] = "c-c-c-combobreaker"; + +char print_buffer[128]; + +struct test_list { + struct test_list *next; + char *buffer; + int value; +}; + +struct number_list { + struct number_list *next; + int value; +}; + +struct test_list *_get_by_buffer(struct test_list *head, char *buffer) +{ + return simple_list_find(head, buffer); +} + +struct test_list *_get_by_value(struct test_list *head, int value) +{ + return simple_list_find(head, value); +} + +struct test_list *_add_test_list(struct test_list **head, char *buffer, int value) +{ + struct test_list *node = simple_list_add_tail(head); + + node->buffer = buffer; + node->value = value; + + return node; +} + +char *_print_result(struct test_list *result) +{ + if (result) { + snprintf(print_buffer, sizeof print_buffer, "%d, %s\n", result->value, result->buffer); + } + else { + snprintf(print_buffer, sizeof print_buffer, "Not found\n"); + } + + return print_buffer; +} + +int _is_equal(struct test_list *node, const int value, const char *buffer) +{ + return node != 0 && node->value == value && !strcmp(node->buffer, buffer); +} + +void test_simple_list_fill(struct test_list *_head) +{ + CHECK_TRUE(_is_equal(_get_by_buffer(_head, bar), 42, bar), "%s", _print_result(_get_by_buffer(_head, bar))); + CHECK_TRUE(_is_equal(_get_by_value(_head, 23), 23, foo), "%s", _print_result(_get_by_value(_head, 23))); +} + +void test_simple_list_remove(struct test_list **__head) +{ + struct test_list *_head; + simple_list_remove(__head, _get_by_buffer(*__head, foo)); + _head = *__head; + + CHECK_TRUE(_is_equal(_head, 42, bar), "%s", _print_result(_head)); + CHECK_TRUE(_is_equal(_head->next, 1337, baz), "%s", _print_result(_head->next)); +} + +void test_simple_list_find(struct test_list *_head) +{ + char buffer[sizeof bar]; + memcpy(buffer, bar, sizeof buffer); + + CHECK_TRUE(_is_equal(simple_list_find_memcmp(_head, buffer), 42, bar), "%s", + _print_result(simple_list_find_memcmp(_head, buffer))); + + CHECK_TRUE(_is_equal(simple_list_find_cmp(_head, buffer, (int ( *)(void *, void *)) strcmp), 42, bar), "%s", + _print_result(simple_list_find_cmp(_head, buffer, (int ( *)(void *, void *)) strcmp))); +} + +void _add_number_list(struct number_list **head, int value) +{ + struct number_list *node = simple_list_add_before(head, value); + node->value = value; +} + +void test_number_list(void) +{ + struct number_list *head = 0; + + _add_number_list(&head, 23); + _add_number_list(&head, 42); + _add_number_list(&head, 17); + _add_number_list(&head, 32); + _add_number_list(&head, 1); + + int prev = 0; + struct number_list *node; + simple_list_for_each(head, node) { + CHECK_TRUE(node->value >= prev, "%d < %d", node->value, prev); + prev = node->value; + } +} + +void test_for_each_remove(void) +{ + struct number_list *head = 0; + + int i = 0; + int max = 11; + + for (i = 1; i < max; ++i) { + if (i == 2) { + _add_number_list(&head, 3); + } + else { + _add_number_list(&head, i); + } + } + + char skipped; + struct number_list *node, *prev; + simple_list_for_each_safe(head, node, prev, skipped) { + if (node->value % 2) { + printf("removing %d\n", node->value); + simple_list_for_each_remove(&head, node, prev); + } + else { + printf("keeping %d\n", node->value); + } + } + + i = 0; + simple_list_for_each(head, node) { + CHECK_TRUE(node->value % 2 == 0, "%d", node->value); + ++i; + } + CHECK_TRUE(i == max / 2 - 1, "missed an entry"); + + simple_list_clear(&head); + + CHECK_TRUE(head == NULL, "list not cleared properly"); +} + +int main(void) +{ + struct test_list *_head = 0; + + _add_test_list(&_head, foo, 23); + _add_test_list(&_head, bar, 42); + _add_test_list(&_head, baz, 1337); + + struct test_list *node; + simple_list_for_each(_head, node) + printf("%s\n", _print_result(node)); + + test_simple_list_fill(_head); + test_simple_list_remove(&_head); + test_simple_list_find(_head); + + test_number_list(); + test_for_each_remove(); + + puts("-----------------"); + printf("End, %d errors\n", fail); + + return fail; +}