diff --git a/Makefile.dep b/Makefile.dep index ed6742868f1b..63a105a39a9c 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -47,6 +47,13 @@ ifneq (,$(filter sixlowpan,$(USEMODULE))) USEMODULE += vtimer endif +ifneq (,$(filter aodvv2,$(USEMODULE))) + USEMODULE += vtimer + USEMODULE += sixlowpan + USEMODULE += oonf_common + USEMODULE += oonf_rfc5444 +endif + ifneq (,$(filter uart0,$(USEMODULE))) USEMODULE += posix endif diff --git a/sys/Makefile b/sys/Makefile index 2a8f2210b87c..6e12c3770e9f 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -44,6 +44,9 @@ endif ifneq (,$(filter routing,$(USEMODULE))) DIRS += net/routing endif +ifneq (,$(filter aodvv2,$(USEMODULE))) + DIRS += net/routing/aodvv2 +endif ifneq (,$(filter ieee802154,$(USEMODULE))) DIRS += net/link_layer/ieee802154 endif diff --git a/sys/net/include/aodvv2/aodvv2.h b/sys/net/include/aodvv2/aodvv2.h new file mode 100644 index 000000000000..86b6b0329063 --- /dev/null +++ b/sys/net/include/aodvv2/aodvv2.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014 Lotte Steenbrink + * + * 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. + */ + +/** + * @defgroup aodvv2 AODVv2 + * @brief The Ad-hoc On-demand Distance Vector routing protocol, version 2 + * @ingroup net + * @{ + * + * @file aodvv2/aodvv2.h + * @brief Interface for the AODVv2 routing protocol + * + * @author Lotte Steenbrink + */ + +#ifndef AODVV2_H_ +#define AODVV2_H_ + +#include "common/netaddr.h" +#include "rfc5444/rfc5444_print.h" + +#include "aodvv2/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize the AODVv2 routing protocol. + */ +void aodv_init(void); + +/** + * @brief Set the metric type. If metric_type does not match any known metric + * types, no changes will be made. + * + * @param[in] metric_type type of new metric + */ +void aodv_set_metric_type(aodvv2_metric_t metric_type); + +#ifdef __cplusplus +} +#endif + +#endif /* AODVV2_H_ */ +/** @} */ diff --git a/sys/net/include/aodvv2/types.h b/sys/net/include/aodvv2/types.h new file mode 100644 index 000000000000..47de03dd6321 --- /dev/null +++ b/sys/net/include/aodvv2/types.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014 Lotte Steenbrink + * + * 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 aodvv2 + * @{ + * + * @file aodvv2/types.h + * @brief data types for the aodvv2 routing protocol + * + * @author Lotte Steenbrink + */ + +#ifndef AODVV2_TYPES_H +#define AODVV2_TYPES_H + +#include "common/netaddr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief AODVv2 metric types. Extend to include alternate metrics. + */ +typedef enum { + HOP_COUNT = 3, /**< see RFC6551*/ +} aodvv2_metric_t; + +typedef uint16_t aodvv2_seqnum_t; + +#define AODVV2_DEFAULT_METRIC_TYPE HOP_COUNT + +/** + * @brief AODVv2 message types + */ +enum rfc5444_msg_type +{ + RFC5444_MSGTYPE_RREQ = 10, + RFC5444_MSGTYPE_RREP = 11, + RFC5444_MSGTYPE_RERR = 12, +}; + +/** + * @brief AODVv2 TLV types + */ +enum rfc5444_tlv_type +{ + RFC5444_MSGTLV_ORIGSEQNUM, + RFC5444_MSGTLV_TARGSEQNUM, + RFC5444_MSGTLV_UNREACHABLE_NODE_SEQNUM, + RFC5444_MSGTLV_METRIC, +}; + +/** + * @brief Data about an OrigNode or TargNode, typically embedded in an + * aodvv2_packet_data struct. + */ +struct node_data +{ + struct netaddr addr; /**< IP address of the node */ + uint8_t metric; /**< Metric value */ + aodvv2_seqnum_t seqnum; /**< Sequence Number */ +}; + +/** + * @brief all data contained in a RREQ or RREP. + */ +struct aodvv2_packet_data +{ + uint8_t hoplimit; /**< Hop limit */ + struct netaddr sender; /**< IP address of the neighboring router + * which sent the RREQ/RREP*/ + aodvv2_metric_t metricType; /**< Metric type */ + struct node_data origNode; /**< Data about the originating node */ + struct node_data targNode; /**< Data about the originating node */ + timex_t timestamp; /**< point at which the packet was (roughly) + * received. Note that this timestamp + * will be set after the packet has been + * successfully parsed. */ +}; + +/** + * @brief Data about an unreachable node to be embedded in a RERR. + */ +struct unreachable_node +{ + struct netaddr addr; /**< IP address */ + aodvv2_seqnum_t seqnum; /**< Sequence Number */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* AODVV2_TYPES_H */ diff --git a/sys/net/routing/aodvv2/Makefile b/sys/net/routing/aodvv2/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/sys/net/routing/aodvv2/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/routing/aodvv2/aodv.c b/sys/net/routing/aodvv2/aodv.c new file mode 100644 index 000000000000..47f216333ef6 --- /dev/null +++ b/sys/net/routing/aodvv2/aodv.c @@ -0,0 +1,478 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014 Lotte Steenbrink + * + * 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 aodvv2 + * @{ + * + * @file aodv.c + * @brief aodvv2 routing protocol + * + * @author Lotte Steenbrink + */ + +#include "debug.h" + +#include "aodv.h" +#include "aodvv2/aodvv2.h" +#include "aodv_debug.h" + +#define ENABLE_DEBUG (0) + +#define UDP_BUFFER_SIZE (128) /** with respect to IEEE 802.15.4's MTU */ +#define RCV_MSG_Q_SIZE (32) /* TODO: check if smaller values work, too */ + +static void _init_addresses(void); +static void _init_sock_snd(void); +static void *_aodv_receiver_thread(void *arg); +static void *_aodv_sender_thread(void *arg); +static void _deep_free_msg_container(struct msg_container *msg_container); +static void _write_packet(struct rfc5444_writer *wr __attribute__ ((unused)), + struct rfc5444_writer_target *iface __attribute__((unused)), + void *buffer, size_t length); + +#ifdef DEBUG_ENABLED +char addr_str[IPV6_MAX_ADDR_STR_LEN]; +static struct netaddr_str nbuf; +#endif + +static char aodv_rcv_stack_buf[KERNEL_CONF_STACKSIZE_MAIN]; +static char aodv_snd_stack_buf[KERNEL_CONF_STACKSIZE_MAIN]; + +static aodvv2_metric_t _metric_type; +static int sender_thread; +static int _sock_snd; +static struct autobuf _hexbuf; +static sockaddr6_t sa_wp; +static ipv6_addr_t _v6_addr_local, _v6_addr_mcast, _v6_addr_loopback; +static struct netaddr na_local; /* the same as _v6_addr_local, but to save us + * constant calls to ipv6_addr_t_to_netaddr()... */ +static struct writer_target *wt; +struct netaddr na_mcast = (struct netaddr){}; + +void aodv_init(void) +{ + AODV_DEBUG("%s()\n", __func__); + + /* TODO: set if_id properly */ + int if_id = 0; + net_if_set_src_address_mode(if_id, NET_IF_TRANS_ADDR_M_SHORT); + + aodv_set_metric_type(AODVV2_DEFAULT_METRIC_TYPE); + _init_addresses(); + _init_sock_snd(); + + /* init ALL the things! \o, */ + seqnum_init(); + routingtable_init(); + clienttable_init(); + + /* every node is its own client. */ + clienttable_add_client(&na_local); + rreqtable_init(); + + /* init reader and writer */ + aodv_packet_reader_init(); + aodv_packet_writer_init(_write_packet); + + /* start listening & enable sending */ + thread_create(aodv_rcv_stack_buf, sizeof(aodv_rcv_stack_buf), PRIORITY_MAIN, + CREATE_STACKTEST, _aodv_receiver_thread, NULL, "_aodv_receiver_thread"); + AODV_DEBUG("listening on port %d\n", HTONS(MANET_PORT)); + sender_thread = thread_create(aodv_snd_stack_buf, sizeof(aodv_snd_stack_buf), + PRIORITY_MAIN, CREATE_STACKTEST, _aodv_sender_thread, + NULL, "_aodv_sender_thread"); + + /* register aodv for routing */ + ipv6_iface_set_routing_provider(aodv_get_next_hop); + +} + +void aodv_set_metric_type(aodvv2_metric_t metric_type) +{ + if (metric_type != AODVV2_DEFAULT_METRIC_TYPE) { + return; + } + _metric_type = metric_type; +} + +void aodv_send_rreq(struct aodvv2_packet_data *packet_data) +{ + AODV_DEBUG("%s()\n", __func__); + + struct aodvv2_packet_data *pd = malloc(sizeof(struct aodvv2_packet_data)); + memcpy(pd, packet_data, sizeof(struct aodvv2_packet_data)); + + struct rreq_rrep_data *rd = malloc(sizeof(struct rreq_rrep_data)); + *rd = (struct rreq_rrep_data) { + .next_hop = &na_mcast, + .packet_data = pd, + }; + + struct msg_container *mc = malloc(sizeof(struct msg_container)); + *mc = (struct msg_container) { + .type = RFC5444_MSGTYPE_RREQ, + .data = rd + }; + + msg_t msg; + msg.content.ptr = (char *) mc; + + msg_try_send(&msg, sender_thread); +} + +void aodv_send_rrep(struct aodvv2_packet_data *packet_data, struct netaddr *next_hop) +{ + AODV_DEBUG("%s()\n", __func__); + + struct aodvv2_packet_data *pd = malloc(sizeof(struct aodvv2_packet_data)); + memcpy(pd, packet_data, sizeof(struct aodvv2_packet_data)); + + struct netaddr *nh = malloc(sizeof(struct netaddr)); + memcpy(nh, next_hop, sizeof(struct netaddr)); + + struct rreq_rrep_data *rd = malloc(sizeof(struct rreq_rrep_data)); + *rd = (struct rreq_rrep_data) { + .next_hop = nh, + .packet_data = pd, + }; + + struct msg_container *mc = malloc(sizeof(struct msg_container)); + *mc = (struct msg_container) { + .type = RFC5444_MSGTYPE_RREP, + .data = rd + }; + + msg_t msg; + msg.content.ptr = (char *) mc; + + msg_try_send(&msg, sender_thread); +} + +void aodv_send_rerr(struct unreachable_node unreachable_nodes[], size_t len, struct netaddr *next_hop) +{ + AODV_DEBUG("%s()\n", __func__); + + struct rerr_data *rerrd = malloc(sizeof(struct rerr_data)); + *rerrd = (struct rerr_data) { + .unreachable_nodes = unreachable_nodes, + .len = len, + .hoplimit = AODVV2_MAX_HOPCOUNT, + .next_hop = next_hop + }; + + struct msg_container *mc2 = malloc(sizeof(struct msg_container)); + *mc2 = (struct msg_container) { + .type = RFC5444_MSGTYPE_RERR, + .data = rerrd + }; + + msg_t msg2; + msg2.content.ptr = (char *) mc2; + + msg_try_send(&msg2, sender_thread); +} + +/* + * init the multicast address all RREQ and RERRS are sent to + * and the local address (source address) of this node + */ +static void _init_addresses(void) +{ + /* init multicast address: set to to a link-local all nodes multicast address */ + ipv6_addr_set_all_nodes_addr(&_v6_addr_mcast); + AODV_DEBUG("my multicast address is: %s\n", + ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &_v6_addr_mcast)); + + /* get best IP for sending */ + ipv6_net_if_get_best_src_addr(&_v6_addr_local, &_v6_addr_mcast); + AODV_DEBUG("my src address is: %s\n", + ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &_v6_addr_local)); + + /* store src & multicast address as netaddr as well for easy interaction + * with oonf based stuff */ + ipv6_addr_t_to_netaddr(&_v6_addr_local, &na_local); + ipv6_addr_t_to_netaddr(&_v6_addr_mcast, &na_mcast); + ipv6_addr_set_loopback_addr(&_v6_addr_loopback); + + /* init sockaddr that write_packet will use to send data */ + sa_wp.sin6_family = AF_INET6; + sa_wp.sin6_port = HTONS(MANET_PORT); +} + +/* init socket communication for sender */ +static void _init_sock_snd(void) +{ + _sock_snd = socket_base_socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + + if (-1 == _sock_snd) { + AODV_DEBUG("Error Creating Socket!\n"); + } +} + +/* Build RREQs, RREPs and RERRs from the information contained in the thread's + * message queue and send them */ +static void *_aodv_sender_thread(void *arg) +{ + (void) arg; + + msg_t msgq[RCV_MSG_Q_SIZE]; + msg_init_queue(msgq, sizeof msgq); + AODV_DEBUG("_aodv_sender_thread initialized.\n"); + + while (true) { + AODV_DEBUG("%s()\n", __func__); + msg_t msg; + msg_receive(&msg); + struct msg_container *mc = (struct msg_container *) msg.content.ptr; + + if (mc->type == RFC5444_MSGTYPE_RREQ) { + struct rreq_rrep_data *rreq_data = (struct rreq_rrep_data *) mc->data; + aodv_packet_writer_send_rreq(rreq_data->packet_data, rreq_data->next_hop); + } + else if (mc->type == RFC5444_MSGTYPE_RREP) { + struct rreq_rrep_data *rrep_data = (struct rreq_rrep_data *) mc->data; + aodv_packet_writer_send_rrep(rrep_data->packet_data, rrep_data->next_hop); + } + else if (mc->type == RFC5444_MSGTYPE_RERR) { + struct rerr_data *rerr_data = (struct rerr_data *) mc->data; + aodv_packet_writer_send_rerr(rerr_data->unreachable_nodes, rerr_data->len, + rerr_data->hoplimit, rerr_data->next_hop); + } + else { + DEBUG("ERROR: Couldn't identify Message\n"); + } + _deep_free_msg_container(mc); + } + + return NULL; +} + +/* receive RREQs, RREPs and RERRs and handle them */ +static void *_aodv_receiver_thread(void *arg) +{ + (void) arg; + + AODV_DEBUG("%s()\n", __func__); + uint32_t fromlen; + char buf_rcv[UDP_BUFFER_SIZE]; + msg_t msg_q[RCV_MSG_Q_SIZE]; + + msg_init_queue(msg_q, RCV_MSG_Q_SIZE); + + sockaddr6_t sa_rcv = { .sin6_family = AF_INET6, + .sin6_port = HTONS(MANET_PORT) + }; + + int sock_rcv = socket_base_socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + + if (-1 == socket_base_bind(sock_rcv, &sa_rcv, sizeof(sa_rcv))) { + DEBUG("Error: bind to receive socket failed!\n"); + socket_base_close(sock_rcv); + } + + AODV_DEBUG("ready to receive data\n"); + while (true) { + int32_t rcv_size = socket_base_recvfrom(sock_rcv, (void *)buf_rcv, UDP_BUFFER_SIZE, 0, + &sa_rcv, &fromlen); + + if (rcv_size < 0) { + AODV_DEBUG("ERROR receiving data!\n"); + } + + AODV_DEBUG("_aodv_receiver_thread() %s:", + ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &_v6_addr_local)); + DEBUG(" UDP packet received from %s\n", + ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &sa_rcv.sin6_addr)); + + struct netaddr _sender; + ipv6_addr_t_to_netaddr(&sa_rcv.sin6_addr, &_sender); + + /* For some reason we sometimes get passed our own packets. drop them. */ + if (netaddr_cmp(&_sender, &na_local) == 0) { + AODV_DEBUG("received our own packet, dropping it.\n"); + aodv_packet_reader_handle_packet((void *) buf_rcv, rcv_size, &_sender); + } + } + + socket_base_close(sock_rcv); + + return NULL; +} + +ipv6_addr_t *aodv_get_next_hop(ipv6_addr_t *dest) +{ + AODV_DEBUG("aodv_get_next_hop() %s:", + ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &_v6_addr_local)); + DEBUG(" getting next hop for %s\n", + ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, dest)); + + struct netaddr _tmp_dest; + ipv6_addr_t_to_netaddr(dest, &_tmp_dest); + timex_t now; + struct unreachable_node unreachable_nodes[AODVV2_MAX_UNREACHABLE_NODES]; + size_t len; + + /* The network stack sometimes asks us for the next hop towards our own IP */ + if (memcmp(dest, &_v6_addr_local, sizeof(ipv6_addr_t)) == 0) { + AODV_DEBUG("That's me, returning loopback\n"); + return &_v6_addr_loopback; + } + + /* + * TODO use ndp_neighbor_get_ll_address() as soon as it's available. + * note: delete check for active/stale/delayed entries, get_ll_address + * does that for us then + */ + ndp_neighbor_cache_t *ndp_nc_entry = ndp_neighbor_cache_search(dest); + struct aodvv2_routing_entry_t *rt_entry = routingtable_get_entry(&_tmp_dest, _metric_type); + + if (ndp_nc_entry != NULL) { + /* Case 2: Broken Link (detected by lower layer) */ + int link_broken = (ndp_nc_entry->state == NDP_NCE_STATUS_INCOMPLETE || + ndp_nc_entry->state == NDP_NCE_STATUS_PROBE) && + (rt_entry != NULL && rt_entry->state != ROUTE_STATE_BROKEN); + + if (link_broken) { + DEBUG("\tNeighbor Cache entry found, but invalid (state: %i). Sending RERR.\n", + ndp_nc_entry->state); + + /* mark all routes (active, idle, expired) that use next_hop as broken + * and add all *Active* routes to the list of unreachable nodes */ + routingtable_break_and_get_all_hopping_over(&_tmp_dest, unreachable_nodes, &len); + + aodv_send_rerr(unreachable_nodes, len, &na_mcast); + return NULL; + } + + DEBUG("[aodvv2][ndp] found NC entry. Returning dest addr.\n"); + return dest; + } + DEBUG("\t[ndp] no entry for addr %s found\n", + ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, dest)); + + if (rt_entry) { + /* Case 1: Undeliverable Packet */ + int packet_indeliverable = rt_entry->state == ROUTE_STATE_BROKEN || + rt_entry->state == ROUTE_STATE_EXPIRED; + if (packet_indeliverable) { + DEBUG("\tRouting table entry found, but invalid (state %i). Sending RERR.\n", + rt_entry->state); + unreachable_nodes[0].addr = _tmp_dest; + unreachable_nodes[0].seqnum = rt_entry->seqnum; + aodv_send_rerr(unreachable_nodes, 1, &na_mcast); + return NULL; + } + + DEBUG("\tfound dest %s in routing table\n", + ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, dest)); + + vtimer_now(&now); + rt_entry->lastUsed = now; + if (rt_entry->state == ROUTE_STATE_IDLE) { + rt_entry->state = ROUTE_STATE_ACTIVE; + } + + /* Currently, there is no way to do this, so I'm doing it the worst + * possible, but safe way: I can't make sure that the current call to + * aodv_get_next_hop() is overridden by another call to aodv_get_next_hop() + * by a thread with higher priority, thus messing up return values if I just + * use a static ipv6_addr_t. + * The following malloc will never be free()'d. TODO: FIX THIS ASAP. + */ + ipv6_addr_t *next_hop = (ipv6_addr_t *) malloc(sizeof(ipv6_addr_t)); + netaddr_to_ipv6_addr_t(&rt_entry->nextHopAddr, next_hop); + + return next_hop; + } + + aodvv2_seqnum_t seqnum = seqnum_get(); + seqnum_inc(); + + struct aodvv2_packet_data rreq_data = (struct aodvv2_packet_data) { + .hoplimit = AODVV2_MAX_HOPCOUNT, + .metricType = _metric_type, + .origNode = (struct node_data) { + .addr = na_local, + .metric = 0, + .seqnum = seqnum, + }, + .targNode = (struct node_data) { + .addr = _tmp_dest, + }, + .timestamp = (timex_t) {0,0} /* this timestamp is never used, it exists + * merely to make the compiler shut up */ + }; + + DEBUG("\tNo route found towards %s, starting route discovery... \n", + ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, dest)); + aodv_send_rreq(&rreq_data); + + return NULL; +} + +/** + * Handle the output of the RFC5444 packet creation process. This callback is + * called by every writer_send_* function. + */ +static void _write_packet(struct rfc5444_writer *wr __attribute__ ((unused)), + struct rfc5444_writer_target *iface __attribute__((unused)), + void *buffer, size_t length) +{ + AODV_DEBUG("%s()\n", __func__); + /* generate hexdump and human readable representation of packet + * and print to console */ + abuf_hexdump(&_hexbuf, "\t", buffer, length); + rfc5444_print_direct(&_hexbuf, buffer, length); + DEBUG("%s", abuf_getptr(&_hexbuf)); + abuf_clear(&_hexbuf); + + /* fetch the address the packet is supposed to be sent to (i.e. to a + * specific node or the multicast address) from the writer_target struct + * iface* is stored in. This is a bit hacky, but it does the trick. */ + wt = container_of(iface, struct writer_target, interface); + netaddr_to_ipv6_addr_t(&wt->target_addr, &sa_wp.sin6_addr); + + /* When originating a RREQ, add it to our RREQ table/update its predecessor */ + if (wt->type == RFC5444_MSGTYPE_RREQ + && netaddr_cmp(&wt->packet_data.origNode.addr, &na_local) == 0) { + AODV_DEBUG("originating RREQ with SeqNum %d towards %s via %s; updating RREQ table...\n", + wt->packet_data.origNode.seqnum, + netaddr_to_string(&nbuf, &wt->packet_data.targNode.addr), + ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &sa_wp.sin6_addr)); + rreqtable_is_redundant(&wt->packet_data); + } + + int bytes_sent = socket_base_sendto(_sock_snd, buffer, length, + 0, &sa_wp, sizeof sa_wp); + + (void) bytes_sent; + AODV_DEBUG("%d bytes sent.\n", bytes_sent); +} + +/* free the matryoshka doll of cobbled-together structs that the sender_thread receives */ +static void _deep_free_msg_container(struct msg_container *mc) +{ + int type = mc->type; + if ((type == RFC5444_MSGTYPE_RREQ) || (type == RFC5444_MSGTYPE_RREP)) { + struct rreq_rrep_data *rreq_rrep_data = (struct rreq_rrep_data *) mc->data; + free(rreq_rrep_data->packet_data); + if (netaddr_cmp(rreq_rrep_data->next_hop, &na_mcast) != 0) { + free(rreq_rrep_data->next_hop); + } + } + else if (type == RFC5444_MSGTYPE_RERR) { + struct rerr_data *rerr_data = (struct rerr_data *) mc->data; + if (netaddr_cmp(rerr_data->next_hop, &na_mcast) != 0) { + free(rerr_data->next_hop); + } + } + free(mc->data); + free(mc); +} diff --git a/sys/net/routing/aodvv2/aodv.h b/sys/net/routing/aodvv2/aodv.h new file mode 100644 index 000000000000..f210b7e22e57 --- /dev/null +++ b/sys/net/routing/aodvv2/aodv.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014 Lotte Steenbrink + * + * 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 aodvv2 + * @{ + * + * @file aodv.h + * @brief aodvv2 routing protocol + * + * @author Lotte Steenbrink + */ + +#ifndef AODV_H_ +#define AODV_H_ + +#include +#include "sixlowpan.h" +#include "kernel.h" +#include "udp.h" +#include "socket_base/socket.h" +#include "net_help.h" +#include "net_if.h" + +#include "aodvv2/types.h" +#include "constants.h" +#include "seqnum.h" +#include "routingtable.h" +#include "utils.h" +#include "reader.h" +#include "writer.h" +#include "thread.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief This struct contains data which needs to be put into a RREQ or RREP. + * It is used to transport this data in a message to the sender_thread. + * @note Please note that it is for internal use only. To send a RREQ or RREP, + * please use the aodv_send_rreq() and aodv_send_rrep() functions. + */ +struct rreq_rrep_data +{ + struct aodvv2_packet_data *packet_data; /**< Data for the RREQ or RREP */ + struct netaddr *next_hop; /**< Next hop to which the RREQ + * or RREP should be sent */ +}; + +/** + * @brief This struct contains data which needs to be put into a RERR. + * It is used to transport this data in a message to the sender_thread. + * @note Please note that it is for internal use only. To send a RERR, + * please use the aodv_send_rerr() function. + */ +struct rerr_data +{ + struct unreachable_node *unreachable_nodes; /**< All unreachable nodes. Beware, + * this is the start of an array */ + size_t len; /**< Length of the unreachable_nodes array */ + int hoplimit; /**< hoplimit for the RERR */ + struct netaddr *next_hop; /**< Next hop to which the RERR + * should be sent */ +}; + + +/** + * @brief This struct holds the data for a RREQ, RREP or RERR (contained + * in a rreq_rrep_data or rerr_data struct) and the next hop the RREQ, RREP + * or RERR should be sent to. It used for message communication with + * the sender_thread. + * @note Please note that it is for internal use only. To send a RERR, + * please use the aodv_send_rerr() function. + */ +struct msg_container +{ + int type; /**< Message type (i.e. one of + * rfc5444_msg_type) */ + void *data; /**< Pointer to the message data + * (i.e. rreq_rrep_data or rerr_data) */ +}; + +/** + * @brief When set as ipv6_iface_routing_provider, this function is called by + * ipv6_sendto() to determine the next hop towards dest. This function + * is non-blocking. + * + * @param[in] dest destination of the packet + * @return Address of the next hop towards dest if there is any, + * NULL if there is none (yet) + */ +ipv6_addr_t *aodv_get_next_hop(ipv6_addr_t *dest); + +/** + * @brief Dispatch a RREQ + * + * @param[in] packet_data Payload of the RREQ + */ +void aodv_send_rreq(struct aodvv2_packet_data *packet_data); + +/** + * @brief Dispatch a RREP + * + * @param[in] packet_data Payload of the RREP + * @param[in] next_hop Address of the next hop the RREP should be sent to + */ +void aodv_send_rrep(struct aodvv2_packet_data *packet_data, struct netaddr *next_hop); + +/** + * @brief Dispatch a RERR + * + * @param[in] unreachable_nodes All nodes that are marked as unreachable + * by this RERR + * @param[in] len Number of unreachable nodes + * @param[in] next_hop Address of the next hop the RERR should be sent to + */ +void aodv_send_rerr(struct unreachable_node unreachable_nodes[], size_t len, + struct netaddr *next_hop); + +#ifdef __cplusplus +} +#endif + +#endif /* AODV_H_ */ diff --git a/sys/net/routing/aodvv2/aodv_debug.h b/sys/net/routing/aodvv2/aodv_debug.h new file mode 100644 index 000000000000..75d681f36923 --- /dev/null +++ b/sys/net/routing/aodvv2/aodv_debug.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +/** + * @addtogroup core_util + * @{ + * + * @ingroup aodvv2 + * @brief Debug-header for aodvv2 debug messages + * + * + * @author Lotte Steenbrink + */ + +#ifndef AODV_DEBUG_H_ +#define AODV_DEBUG_H_ + +#include +#include "sched.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#ifdef DEBUG_ENABLED +#define ENABLE_AODV_DEBUG (1) +#endif + +/** + * @brief Print aodvv2 specific debug information to std-out with [aodvv2] prefix + * + */ +#if ENABLE_AODV_DEBUG +#include "tcb.h" +#define AODV_DEBUG(...) \ + do { \ + printf("[aodvv2] "); \ + printf(__VA_ARGS__); \ + } while (0) +#else +#define AODV_DEBUG(...) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* AODVV2_DEBUG_H_*/ +/** @} */ diff --git a/sys/net/routing/aodvv2/constants.h b/sys/net/routing/aodvv2/constants.h new file mode 100644 index 000000000000..3567ccfce995 --- /dev/null +++ b/sys/net/routing/aodvv2/constants.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014 Lotte Steenbrink + * + * 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 aodvv2 + * @{ + * + * @file constants.h + * @brief constants for the aodvv2 routing protocol + * + * @author Lotte Steenbrink + */ + +#ifndef AODVV2_CONSTANTS_H_ +#define AODVV2_CONSTANTS_H_ + +#include "aodvv2/types.h" + +#include "common/netaddr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define MANET_PORT 269 /** RFC5498 */ + +enum aodvv2_constants { + AODVV2_MAX_HOPCOUNT = 250, /**< see AODVv2 draft, section 14.2.*/ + AODVV2_MAX_ROUTING_ENTRIES = 255, /**< maximum number of entries + * in the routing table */ + AODVV2_ACTIVE_INTERVAL = 5, /**< seconds */ + AODVV2_MAX_IDLETIME = 250, /**< seconds */ + AODVV2_MAX_SEQNUM_LIFETIME = 300, /**< seconds */ + AODVV2_MAX_UNREACHABLE_NODES = 15, /**< TODO: choose value (wisely) */ +}; + +/** + * @brief TLV type array indices + */ +enum tlv_index +{ + TLV_ORIGSEQNUM, + TLV_TARGSEQNUM, + TLV_UNREACHABLE_NODE_SEQNUM, + TLV_METRIC, +}; + +/* my multicast address */ +extern struct netaddr na_mcast; + +#ifdef __cplusplus +} +#endif + +#endif /* AODVV2_CONSTANTS_H_ */ diff --git a/sys/net/routing/aodvv2/reader.c b/sys/net/routing/aodvv2/reader.c new file mode 100644 index 000000000000..1333a807fb72 --- /dev/null +++ b/sys/net/routing/aodvv2/reader.c @@ -0,0 +1,754 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014 Lotte Steenbrink + * + * 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 aodvv2 + * @{ + * + * @file reader.c + * @brief reading and handling of RFC5444 aodvv2 messages + * + * @author Lotte Steenbrink + */ + +#ifdef RIOT +#include "net_help.h" +#endif + +#include "debug.h" + +#include "reader.h" +#include "aodv_debug.h" + +#define ENABLE_DEBUG (0) + +#define VERBOSE_DEBUG (0) +#if VERBOSE_DEBUG +#define VDEBUG(...) AODV_DEBUG(__VA_ARGS__) +#else +#define VDEBUG(...) +#endif + +static enum rfc5444_result _cb_rreq_blocktlv_addresstlvs_okay( + struct rfc5444_reader_tlvblock_context *cont); +static enum rfc5444_result _cb_rreq_blocktlv_messagetlvs_okay( + struct rfc5444_reader_tlvblock_context *cont); +static enum rfc5444_result _cb_rreq_end_callback( + struct rfc5444_reader_tlvblock_context *cont, bool dropped); + +static enum rfc5444_result _cb_rrep_blocktlv_addresstlvs_okay( + struct rfc5444_reader_tlvblock_context *cont); +static enum rfc5444_result _cb_rrep_blocktlv_messagetlvs_okay( + struct rfc5444_reader_tlvblock_context *cont); +static enum rfc5444_result _cb_rrep_end_callback( + struct rfc5444_reader_tlvblock_context *cont, bool dropped); + +static enum rfc5444_result _cb_rerr_blocktlv_addresstlvs_okay( + struct rfc5444_reader_tlvblock_context *cont); +static enum rfc5444_result _cb_rerr_blocktlv_messagetlvs_okay( + struct rfc5444_reader_tlvblock_context *cont); +static enum rfc5444_result _cb_rerr_end_callback( + struct rfc5444_reader_tlvblock_context *cont, bool dropped); + +/* helper functions */ +static uint8_t _get_link_cost(aodvv2_metric_t metricType); +static uint8_t _get_max_metric(aodvv2_metric_t metricType); +static void _update_metric(aodvv2_metric_t metricType, uint8_t *metric); + +/* This is where we store data gathered from packets */ +static struct aodvv2_packet_data packet_data; +static struct unreachable_node unreachable_nodes[AODVV2_MAX_UNREACHABLE_NODES]; +static int num_unreachable_nodes; + +static struct rfc5444_reader reader; +#ifdef DEBUG_ENABLED +static struct netaddr_str nbuf; +#endif + +/* + * Message consumer, will be called once for every message of + * type RFC5444_MSGTYPE_RREQ that contains all the mandatory message TLVs + */ +static struct rfc5444_reader_tlvblock_consumer _rreq_consumer = +{ + .msg_id = RFC5444_MSGTYPE_RREQ, + .block_callback = _cb_rreq_blocktlv_messagetlvs_okay, + .end_callback = _cb_rreq_end_callback, +}; + +/* + * Address consumer. Will be called once for every address in a message of + * type RFC5444_MSGTYPE_RREQ. + */ +static struct rfc5444_reader_tlvblock_consumer _rreq_address_consumer = +{ + .msg_id = RFC5444_MSGTYPE_RREQ, + .addrblock_consumer = true, + .block_callback = _cb_rreq_blocktlv_addresstlvs_okay, +}; + +/* + * Message consumer, will be called once for every message of + * type RFC5444_MSGTYPE_RREP that contains all the mandatory message TLVs + */ +static struct rfc5444_reader_tlvblock_consumer _rrep_consumer = +{ + .msg_id = RFC5444_MSGTYPE_RREP, + .block_callback = _cb_rrep_blocktlv_messagetlvs_okay, + .end_callback = _cb_rrep_end_callback, +}; + +/* + * Address consumer. Will be called once for every address in a message of + * type RFC5444_MSGTYPE_RREP. + */ +static struct rfc5444_reader_tlvblock_consumer _rrep_address_consumer = +{ + .msg_id = RFC5444_MSGTYPE_RREP, + .addrblock_consumer = true, + .block_callback = _cb_rrep_blocktlv_addresstlvs_okay, +}; + +/* + * Message consumer, will be called once for every message of + * type RFC5444_MSGTYPE_RERR that contains all the mandatory message TLVs + */ +static struct rfc5444_reader_tlvblock_consumer _rerr_consumer = +{ + .msg_id = RFC5444_MSGTYPE_RERR, + .block_callback = _cb_rerr_blocktlv_messagetlvs_okay, + .end_callback = _cb_rerr_end_callback, +}; + +/* + * Address consumer. Will be called once for every address in a message of + * type RFC5444_MSGTYPE_RERR. + */ +static struct rfc5444_reader_tlvblock_consumer _rerr_address_consumer = +{ + .msg_id = RFC5444_MSGTYPE_RERR, + .addrblock_consumer = true, + .block_callback = _cb_rerr_blocktlv_addresstlvs_okay, +}; + + +/* + * Address consumer entries definition + * TLV types RFC5444_MSGTLV__SEQNUM and RFC5444_MSGTLV_METRIC + */ +static struct rfc5444_reader_tlvblock_consumer_entry _rreq_rrep_address_consumer_entries[] = +{ + [RFC5444_MSGTLV_ORIGSEQNUM] = { .type = RFC5444_MSGTLV_ORIGSEQNUM}, + [RFC5444_MSGTLV_TARGSEQNUM] = { .type = RFC5444_MSGTLV_TARGSEQNUM}, + [RFC5444_MSGTLV_METRIC] = { .type = RFC5444_MSGTLV_METRIC } +}; + +/* + * Address consumer entries definition + * TLV types RFC5444_MSGTLV__SEQNUM and RFC5444_MSGTLV_METRIC + */ +static struct rfc5444_reader_tlvblock_consumer_entry _rerr_address_consumer_entries[] = +{ + [RFC5444_MSGTLV_UNREACHABLE_NODE_SEQNUM] = { + .type = RFC5444_MSGTLV_UNREACHABLE_NODE_SEQNUM + }, +}; + +/** + * This block callback is called for every address + * + * @param cont + * @return + */ +static enum rfc5444_result _cb_rreq_blocktlv_messagetlvs_okay(struct rfc5444_reader_tlvblock_context *cont) +{ + VDEBUG("%s()\n", __func__); + + if (!cont->has_hoplimit) { + DEBUG("\tERROR: missing hop limit\n"); + return RFC5444_DROP_PACKET; + } + + packet_data.hoplimit = cont->hoplimit; + if (packet_data.hoplimit == 0) { + DEBUG("\tERROR: Hoplimit is 0.\n"); + return RFC5444_DROP_PACKET; + } + packet_data.hoplimit--; + return RFC5444_OKAY; +} + +/** + * This block callback is called for every address of a RREQ Message. + * + * @param cont + * @return + */ +static enum rfc5444_result _cb_rreq_blocktlv_addresstlvs_okay(struct rfc5444_reader_tlvblock_context *cont) +{ +#ifdef DEBUG_ENABLED + struct netaddr_str nbuf; +#endif + struct rfc5444_reader_tlvblock_entry *tlv; + bool is_origNode_addr = false; + bool is_targNode_addr = false; + + VDEBUG("%s()\n", __func__); + DEBUG("\taddr: %s\n", netaddr_to_string(&nbuf, &cont->addr)); + + /* handle OrigNode SeqNum TLV */ + tlv = _rreq_rrep_address_consumer_entries[RFC5444_MSGTLV_ORIGSEQNUM].tlv; + if (tlv) { + DEBUG("\ttlv RFC5444_MSGTLV_ORIGSEQNUM: %d\n", *tlv->single_value); + is_origNode_addr = true; + packet_data.origNode.addr = cont->addr; + packet_data.origNode.seqnum = *tlv->single_value; + } + + /* handle TargNode SeqNum TLV */ + tlv = _rreq_rrep_address_consumer_entries[RFC5444_MSGTLV_TARGSEQNUM].tlv; + if (tlv) { + DEBUG("\ttlv RFC5444_MSGTLV_TARGSEQNUM: %d\n", *tlv->single_value); + is_targNode_addr = true; + packet_data.targNode.addr = cont->addr; + packet_data.targNode.seqnum = *tlv->single_value; + } + if (!tlv && !is_origNode_addr) { + /* assume that tlv missing => targNode Address */ + is_targNode_addr = true; + packet_data.targNode.addr = cont->addr; + } + if (!is_origNode_addr && !is_targNode_addr) { + DEBUG("\tERROR: mandatory RFC5444_MSGTLV_ORIGSEQNUM TLV missing.\n"); + return RFC5444_DROP_PACKET; + } + + /* handle Metric TLV */ + /* cppcheck: suppress false positive on non-trivially initialized arrays. + * this is a known bug: http://trac.cppcheck.net/ticket/5497 */ + /* cppcheck-suppress arrayIndexOutOfBounds */ + tlv = _rreq_rrep_address_consumer_entries[RFC5444_MSGTLV_METRIC].tlv; + if (!tlv && is_origNode_addr) { + DEBUG("\tERROR: Missing or unknown metric TLV.\n"); + return RFC5444_DROP_PACKET; + } + if (tlv) { + if (!is_origNode_addr) { + DEBUG("\tERROR: Metric TLV belongs to wrong address.\n"); + return RFC5444_DROP_PACKET; + } + VDEBUG("\ttlv RFC5444_MSGTLV_METRIC val: %d, exttype: %d\n", + *tlv->single_value, tlv->type_ext); + packet_data.metricType = tlv->type_ext; + packet_data.origNode.metric = *tlv->single_value; + } + return RFC5444_OKAY; +} + +/** + * This callback is called every time the _rreq_consumer finishes reading a + * packet. + * @param cont + * @param dropped indicates whether the packet has been dropped previously by + * another callback + */ +static enum rfc5444_result _cb_rreq_end_callback( + struct rfc5444_reader_tlvblock_context *cont, bool dropped) +{ + (void) cont; + + struct aodvv2_routing_entry_t *rt_entry; + timex_t now; + uint8_t link_cost = _get_link_cost(packet_data.metricType); + + /* Check if packet contains the required information */ + if (dropped) { + DEBUG("\t Dropping packet.\n"); + return RFC5444_DROP_PACKET; + } + if ((packet_data.origNode.addr._type == AF_UNSPEC) || !packet_data.origNode.seqnum) { + DEBUG("\tERROR: missing OrigNode Address or SeqNum. Dropping packet.\n"); + return RFC5444_DROP_PACKET; + } + if (packet_data.targNode.addr._type == AF_UNSPEC) { + DEBUG("\tERROR: missing TargNode Address. Dropping packet.\n"); + return RFC5444_DROP_PACKET; + } + if (packet_data.hoplimit == 0) { + DEBUG("\tERROR: Hoplimit is 0. Dropping packet.\n"); + return RFC5444_DROP_PACKET; + } + if ((_get_max_metric(packet_data.metricType) - link_cost) + <= packet_data.origNode.metric) { + DEBUG("\tMetric Limit reached. Dropping packet.\n"); + return RFC5444_DROP_PACKET; + } + + /* + The incoming RREQ MUST be checked against previously received + information from the RREQ Table Section 7.6. If the information + in the incoming RteMsg is redundant, then then no further action + is taken. + */ + if (rreqtable_is_redundant(&packet_data)) { + DEBUG("\tPacket is redundant. Dropping Packet. %i\n", RFC5444_DROP_PACKET); + return RFC5444_DROP_PACKET; + } + + _update_metric(packet_data.metricType, &packet_data.origNode.metric); + vtimer_now(&now); + packet_data.timestamp = now; + + /* for every relevant + * address (RteMsg.Addr) in the RteMsg, HandlingRtr searches its route + * table to see if there is a route table entry with the same MetricType + * of the RteMsg, matching RteMsg.Addr. + */ + + rt_entry = routingtable_get_entry(&packet_data.origNode.addr, packet_data.metricType); + + if (!rt_entry || (rt_entry->metricType != packet_data.metricType)) { + /* CAUTION SUPER HACKY FIX FIXME ASAP + problem: sometimes we get broadcasted RREQs from 2 hop neighbors and then + AODVv2 gets super confused when they're not in the routing table and starts a + Route discovery to find them and all hell breaks loose. let's see if we can fix + this (horribly). + + (another fix would be to stop bouncing the RREP back to the sender and asking + the routing table for the next hop (or just send towards TargNode and let the + network stack figure out the rest?)) + TODO evaluate that + */ + + ipv6_addr_t sender_tmp; + netaddr_to_ipv6_addr_t(&packet_data.sender, &sender_tmp); + ndp_neighbor_cache_t *ndp_nc_entry = ndp_neighbor_cache_search(&sender_tmp); + + if (ndp_nc_entry == NULL) { + DEBUG("OH NOES! No bidirectional link to sender. Dropping packet.\n"); + return RFC5444_DROP_PACKET; + } + /* HACKY FIX ENDS HERE */ + + VDEBUG("\tCreating new Routing Table entry...\n"); + + struct aodvv2_routing_entry_t *tmp_rt_entry = (struct aodvv2_routing_entry_t *) + malloc(sizeof(struct aodvv2_routing_entry_t)); + memset(tmp_rt_entry, 0, sizeof(*tmp_rt_entry)); + + routingtable_fill_routing_entry_t_rreq(&packet_data, tmp_rt_entry, link_cost); + routingtable_add_entry(tmp_rt_entry); + + free(tmp_rt_entry); + } + else { + if (!routingtable_offers_improvement(rt_entry, &packet_data.origNode)) { + DEBUG("\tPacket offers no improvement over known route. Dropping Packet.\n"); + return RFC5444_DROP_PACKET; + } + /* The incoming routing information is better than existing routing + * table information and SHOULD be used to improve the route table. */ + VDEBUG("\tUpdating Routing Table entry...\n"); + routingtable_fill_routing_entry_t_rreq(&packet_data, rt_entry, link_cost); + } + + /* + * If TargNode is a client of the router receiving the RREQ, then the + * router generates a RREP message as specified in Section 7.4, and + * subsequently processing for the RREQ is complete. Otherwise, + * processing continues as follows. + */ + if (clienttable_is_client(&packet_data.targNode.addr)) { + AODV_DEBUG("TargNode is in client list, sending RREP\n"); + + /* make sure to start with a clean metric value */ + packet_data.targNode.metric = 0; + aodv_send_rrep(&packet_data, &packet_data.sender); + } + else { + AODV_DEBUG("I am not TargNode, forwarding RREQ\n"); + aodv_send_rreq(&packet_data); + } + return RFC5444_OKAY; +} + +/** + * This block callback is called for every address + * + * @param cont + * @return + */ +static enum rfc5444_result _cb_rrep_blocktlv_messagetlvs_okay(struct rfc5444_reader_tlvblock_context *cont) +{ + VDEBUG("%s()\n", __func__); + + if (!cont->has_hoplimit) { + VDEBUG("\tERROR: missing hop limit\n"); + return RFC5444_DROP_PACKET; + } + + packet_data.hoplimit = cont->hoplimit; + if (packet_data.hoplimit == 0) { + VDEBUG("\tERROR: Hoplimit is 0.\n"); + return RFC5444_DROP_PACKET; + } + + packet_data.hoplimit--; + return RFC5444_OKAY; +} + +/** + * This block callback is called for every address of a RREP Message. + * + * @param cont + * @return + */ +static enum rfc5444_result _cb_rrep_blocktlv_addresstlvs_okay(struct rfc5444_reader_tlvblock_context *cont) +{ +#ifdef DEBUG_ENABLED + struct netaddr_str nbuf; +#endif + struct rfc5444_reader_tlvblock_entry *tlv; + bool is_targNode_addr = false; + + VDEBUG("%s()\n", __func__); + VDEBUG("\taddr: %s\n", netaddr_to_string(&nbuf, &cont->addr)); + + /* handle TargNode SeqNum TLV */ + tlv = _rreq_rrep_address_consumer_entries[RFC5444_MSGTLV_TARGSEQNUM].tlv; + if (tlv) { + VDEBUG("\ttlv RFC5444_MSGTLV_TARGSEQNUM: %d\n", *tlv->single_value); + is_targNode_addr = true; + packet_data.targNode.addr = cont->addr; + packet_data.targNode.seqnum = *tlv->single_value; + } + + /* handle OrigNode SeqNum TLV */ + tlv = _rreq_rrep_address_consumer_entries[RFC5444_MSGTLV_ORIGSEQNUM].tlv; + if (tlv) { + VDEBUG("\ttlv RFC5444_MSGTLV_ORIGSEQNUM: %d\n", *tlv->single_value); + is_targNode_addr = false; + packet_data.origNode.addr = cont->addr; + packet_data.origNode.seqnum = *tlv->single_value; + } + if (!tlv && !is_targNode_addr) { + DEBUG("\tERROR: mandatory SeqNum TLV missing.\n"); + return RFC5444_DROP_PACKET; + } + + /* handle Metric TLV */ + /* cppcheck: suppress false positive on non-trivially initialized arrays. + * this is a known bug: http://trac.cppcheck.net/ticket/5497 */ + /* cppcheck-suppress arrayIndexOutOfBounds */ + tlv = _rreq_rrep_address_consumer_entries[RFC5444_MSGTLV_METRIC].tlv; + if (!tlv && is_targNode_addr) { + DEBUG("\tERROR: Missing or unknown metric TLV.\n"); + return RFC5444_DROP_PACKET; + } + if (tlv) { + if (!is_targNode_addr) { + DEBUG("\tERROR: metric TLV belongs to wrong address.\n"); + return RFC5444_DROP_PACKET; + } + VDEBUG("\ttlv RFC5444_MSGTLV_METRIC val: %d, exttype: %d\n", + *tlv->single_value, tlv->type_ext); + packet_data.metricType = tlv->type_ext; + packet_data.origNode.metric = *tlv->single_value; + } + return RFC5444_OKAY; +} + +/** + * This callback is called every time the _rreq_consumer finishes reading a + * packet. + * @param cont + * @param dropped indicates wehther the packet has been dropped previously by + * another callback + */ +static enum rfc5444_result _cb_rrep_end_callback( + struct rfc5444_reader_tlvblock_context *cont, bool dropped) +{ + (void) cont; + + VDEBUG("%s()\n", __func__); + + struct aodvv2_routing_entry_t *rt_entry; +#ifdef DEBUG_ENABLED + struct netaddr_str nbuf; +#endif + timex_t now; + uint8_t link_cost = _get_link_cost(packet_data.metricType); + + /* Check if packet contains the required information */ + if (dropped) { + DEBUG("\t Dropping packet.\n"); + return RFC5444_DROP_PACKET; + } + if ((packet_data.origNode.addr._type == AF_UNSPEC) + || !packet_data.origNode.seqnum) { + DEBUG("\tERROR: missing OrigNode Address or SeqNum. Dropping packet.\n"); + return RFC5444_DROP_PACKET; + } + if ((packet_data.targNode.addr._type == AF_UNSPEC) + || !packet_data.targNode.seqnum) { + DEBUG("\tERROR: missing TargNode Address or SeqNum. Dropping packet.\n"); + return RFC5444_DROP_PACKET; + } + if ((_get_max_metric(packet_data.metricType) - link_cost) + <= packet_data.targNode.metric) { + DEBUG("\tMetric Limit reached. Dropping packet.\n"); + return RFC5444_DROP_PACKET; + } + + _update_metric(packet_data.metricType, &packet_data.targNode.metric); + vtimer_now(&now); + packet_data.timestamp = now; + + /* for every relevant address (RteMsg.Addr) in the RteMsg, HandlingRtr + searches its route table to see if there is a route table entry with the + same MetricType of the RteMsg, matching RteMsg.Addr. */ + + rt_entry = routingtable_get_entry(&packet_data.targNode.addr, packet_data.metricType); + + if (!rt_entry || (rt_entry->metricType != packet_data.metricType)) { + /* CAUTION SUPER HACKY FIX FIXME ASAP + problem: sometimes we get broadcasted RREQs from 2 hop neighbors and then + AODVv2 gets super confused when they're not in the routing table and starts a + Route discovery to find them and all hell breaks loose. let's see if we can fix + this (horribly). + + (another fix would be to stop bouncing the RREP back to the sender and asking + the routing table for the next hop (or just send towards TargNode and let the network stack figure out the rest?)) + TODO evaluate that + */ + + ipv6_addr_t sender_tmp; + netaddr_to_ipv6_addr_t(&packet_data.sender, &sender_tmp); + ndp_neighbor_cache_t *ndp_nc_entry = ndp_neighbor_cache_search(&sender_tmp); + + if (ndp_nc_entry == NULL) { + DEBUG("OH NOES! No bidirectional link to sender. Dropping packet.\n"); + return RFC5444_DROP_PACKET; + } + /* HACKY FIX ENDS HERE */ + VDEBUG("\tCreating new Routing Table entry...\n"); + + struct aodvv2_routing_entry_t *tmp_rt_entry = (struct aodvv2_routing_entry_t *) + malloc(sizeof(struct aodvv2_routing_entry_t)); + memset(tmp_rt_entry, 0, sizeof(*tmp_rt_entry)); + + routingtable_fill_routing_entry_t_rrep(&packet_data, tmp_rt_entry, link_cost); + routingtable_add_entry(tmp_rt_entry); + + free(tmp_rt_entry); + } + else { + if (!routingtable_offers_improvement(rt_entry, &packet_data.targNode)) { + DEBUG("\tPacket offers no improvement over known route. Dropping Packet.\n"); + return RFC5444_DROP_PACKET; + } + /* The incoming routing information is better than existing routing + * table information and SHOULD be used to improve the route table. */ + VDEBUG("\tUpdating Routing Table entry...\n"); + routingtable_fill_routing_entry_t_rrep(&packet_data, rt_entry, link_cost); + } + + /* If HandlingRtr is RREQ_Gen then the RREP satisfies RREQ_Gen's + earlier RREQ, and RREP processing is completed. Any packets + buffered for OrigNode should be transmitted. */ + if (clienttable_is_client(&packet_data.origNode.addr)) { +#ifdef DEBUG_ENABLED + static struct netaddr_str nbuf2; +#endif + + DEBUG("\t{%" PRIu32 ":%" PRIu32 "} %s: This is my RREP (SeqNum: %d). We are done here, thanks %s!\n", + now.seconds, now.microseconds, netaddr_to_string(&nbuf, &packet_data.origNode.addr), + packet_data.origNode.seqnum, netaddr_to_string(&nbuf2, &packet_data.targNode.addr)); + } + + else { + /* If HandlingRtr is not RREQ_Gen then the outgoing RREP is sent to the + * Route.NextHopAddress for the RREP.AddrBlk[OrigNodeNdx]. */ + AODV_DEBUG("Not my RREP, passing it on to the next hop\n"); + aodv_send_rrep(&packet_data, + routingtable_get_next_hop(&packet_data.origNode.addr,packet_data.metricType)); + } + return RFC5444_OKAY; +} + +static enum rfc5444_result _cb_rerr_blocktlv_messagetlvs_okay(struct rfc5444_reader_tlvblock_context *cont) +{ + VDEBUG("%s()\n", __func__); + + if (!cont->has_hoplimit) { + VDEBUG("\tERROR: missing hop limit\n"); + return RFC5444_DROP_PACKET; + } + + packet_data.hoplimit = cont->hoplimit; + if (packet_data.hoplimit == 0) { + VDEBUG("\tERROR: Hoplimit is 0.\n"); + return RFC5444_DROP_PACKET; + } + + packet_data.hoplimit--; + + /* prepare buffer for unreachable nodes */ + num_unreachable_nodes = 0; + for (unsigned i = 0; i < AODVV2_MAX_UNREACHABLE_NODES; i++) { + memset(&unreachable_nodes[i], 0, sizeof(unreachable_nodes[i])); + } + return RFC5444_OKAY; +} + +static enum rfc5444_result _cb_rerr_blocktlv_addresstlvs_okay(struct rfc5444_reader_tlvblock_context *cont) +{ +#ifdef DEBUG_ENABLED + struct netaddr_str nbuf; +#endif + struct aodvv2_routing_entry_t *unreachable_entry; + struct rfc5444_reader_tlvblock_entry *tlv; + + VDEBUG("%s()\n", __func__); + VDEBUG("\tmessage type: %d\n", cont->type); + VDEBUG("\taddr: %s\n", netaddr_to_string(&nbuf, &cont->addr)); + + /* Out of buffer size for more unreachable nodes. We're screwed, basically. */ + if (num_unreachable_nodes == AODVV2_MAX_UNREACHABLE_NODES) { + return RFC5444_OKAY; + } + + /* gather packet data */ + packet_data.origNode.addr = cont->addr; + + /* handle this unreachable node's SeqNum TLV */ + /* cppcheck: suppress false positive on non-trivially initialized arrays. + * this is a known bug: http://trac.cppcheck.net/ticket/5497 */ + /* cppcheck-suppress arrayIndexOutOfBounds */ + tlv = _rerr_address_consumer_entries[RFC5444_MSGTLV_UNREACHABLE_NODE_SEQNUM].tlv; + if (tlv) { + VDEBUG("\ttlv RFC5444_MSGTLV_UNREACHABLE_NODE_SEQNUM: %d\n", *tlv->single_value); + packet_data.origNode.seqnum = *tlv->single_value; + } + + /* Check if there is an entry for unreachable node in our routing table */ + unreachable_entry = routingtable_get_entry(&packet_data.origNode.addr, packet_data.metricType); + if (unreachable_entry) { + VDEBUG("\t found possibly unreachable entry.\n"); + + /* check if route to unreachable node has to be marked as broken and RERR has to be forwarded */ + if (netaddr_cmp(&unreachable_entry->nextHopAddr, &packet_data.sender) == 0 + && (!tlv || seqnum_cmp(unreachable_entry->seqnum, packet_data.origNode.seqnum) == 0)) { + unreachable_entry->state = ROUTE_STATE_BROKEN; + unreachable_nodes[num_unreachable_nodes].addr = packet_data.origNode.addr; + unreachable_nodes[num_unreachable_nodes].seqnum = packet_data.origNode.seqnum; + num_unreachable_nodes++; + } + } + return RFC5444_OKAY; +} + +static enum rfc5444_result _cb_rerr_end_callback(struct rfc5444_reader_tlvblock_context *cont, bool dropped) +{ + (void) cont; + + if (dropped) { + VDEBUG("\tDropping packet.\n"); + return RFC5444_DROP_PACKET; + } + + if (num_unreachable_nodes == 0) { + VDEBUG("\tNo unreachable nodes from my routing table. Dropping Packet.\n"); + return RFC5444_DROP_PACKET; + } + /* gather all unreachable nodes and put them into a RERR */ + aodv_send_rerr(unreachable_nodes, num_unreachable_nodes, &na_mcast); + return RFC5444_OKAY; +} + +void aodv_packet_reader_init(void) +{ + VDEBUG("%s()\n", __func__); + + /* initialize reader */ + rfc5444_reader_init(&reader); + + /* register message consumers. We have no message TLVs, so we can leave the + * rfc5444_reader_tlvblock_consumer_entry empty */ + rfc5444_reader_add_message_consumer(&reader, &_rreq_consumer, + NULL, 0); + rfc5444_reader_add_message_consumer(&reader, &_rrep_consumer, + NULL, 0); + rfc5444_reader_add_message_consumer(&reader, &_rerr_consumer, + NULL, 0); + + /* register address consumer */ + rfc5444_reader_add_message_consumer(&reader, &_rreq_address_consumer, + _rreq_rrep_address_consumer_entries, + ARRAYSIZE(_rreq_rrep_address_consumer_entries)); + rfc5444_reader_add_message_consumer(&reader, &_rrep_address_consumer, + _rreq_rrep_address_consumer_entries, + ARRAYSIZE(_rreq_rrep_address_consumer_entries)); + rfc5444_reader_add_message_consumer(&reader, &_rerr_address_consumer, + _rerr_address_consumer_entries, + ARRAYSIZE(_rerr_address_consumer_entries)); +} + +void aodv_packet_reader_cleanup(void) +{ + VDEBUG("%s()\n", __func__); + rfc5444_reader_cleanup(&reader); +} + +int aodv_packet_reader_handle_packet(void *buffer, size_t length, struct netaddr *sender) +{ + AODV_DEBUG("%s()\n", __func__); + memcpy(&packet_data.sender, sender, sizeof(*sender)); + DEBUG("\t sender: %s\n", netaddr_to_string(&nbuf, &packet_data.sender)); + + return rfc5444_reader_handle_packet(&reader, buffer, length); +} + +/*============= HELPER FUNCTIONS =============================================*/ + +/* + * Cost(L): Get Cost of a Link regarding the specified metric. + * (currently only AODVV2_DEFAULT_METRIC_TYPE (HopCt) implemented) + * returns cost if metric is known, NULL otherwise + */ +static uint8_t _get_link_cost(aodvv2_metric_t metricType) +{ + if (metricType == AODVV2_DEFAULT_METRIC_TYPE) { + return 1; + } + return 0; +} + +/* + * MAX_METRIC[MetricType]: + * returns maximum value of the given metric if metric is known, NULL otherwise. + */ +static uint8_t _get_max_metric(aodvv2_metric_t metricType) +{ + if (metricType == AODVV2_DEFAULT_METRIC_TYPE) { + return AODVV2_MAX_HOPCOUNT; + } + return 0; +} + +/* + * Calculate a metric's new value according to the specified MetricType + * (currently only implemented for AODVV2_DEFAULT_METRIC_TYPE (HopCt)) + */ +static void _update_metric(aodvv2_metric_t metricType, uint8_t *metric) +{ + if (metricType == AODVV2_DEFAULT_METRIC_TYPE){ + *metric = *metric + 1; + } +} diff --git a/sys/net/routing/aodvv2/reader.h b/sys/net/routing/aodvv2/reader.h new file mode 100644 index 000000000000..07fe13384393 --- /dev/null +++ b/sys/net/routing/aodvv2/reader.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014 Lotte Steenbrink + * + * 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 aodvv2 + * @{ + * + * @file reader.h + * @brief reading and handling of RFC5444 aodvv2 messages + * + * @author Lotte Steenbrink + */ + +#ifndef AODVV2_READER_H_ +#define AODVV2_READER_H_ + +#include +#include + +#include "common/netaddr.h" +#include "rfc5444/rfc5444_reader.h" + +#include "utils.h" +#include "routingtable.h" +#include "constants.h" +#include "seqnum.h" +#include "aodv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize reader. + */ +void aodv_packet_reader_init(void); + +/** + * @brief Clean up after reader. Only needs to be called upon shutdown. + */ +void aodv_packet_reader_cleanup(void); + +/** + * @brief Read data buffer as RFC5444 packet and handle the data it contains + * + * @param[in] buffer Data to be read and handled + * @param[in] length Length of data + * @param[in] sender Address of the node from which the packet was received + */ +int aodv_packet_reader_handle_packet(void *buffer, size_t length, struct netaddr *sender); + +#ifdef __cplusplus +} +#endif + +#endif /* AODVV2_READER_H_ */ diff --git a/sys/net/routing/aodvv2/routingtable.c b/sys/net/routing/aodvv2/routingtable.c new file mode 100644 index 000000000000..e7a7316ef2fc --- /dev/null +++ b/sys/net/routing/aodvv2/routingtable.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014 Lotte Steenbrink + * + * 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 aodvv2 + * @{ + * + * @file routing.c + * @brief Cobbled-together routing table. + * + * @author Lotte Steenbrink + */ + +#include "debug.h" + +#include "routingtable.h" +#include "aodv_debug.h" + +#define ENABLE_DEBUG (0) + +/* helper functions */ +static void _reset_entry_if_stale(uint8_t i); + +static struct aodvv2_routing_entry_t routing_table[AODVV2_MAX_ROUTING_ENTRIES]; +static timex_t null_time, max_seqnum_lifetime, active_interval, max_idletime, validity_t; +timex_t now; +#ifdef DEBUG_ENABLED +static struct netaddr_str nbuf; +#endif + +void routingtable_init(void) +{ + null_time = timex_set(0, 0); + max_seqnum_lifetime = timex_set(AODVV2_MAX_SEQNUM_LIFETIME, 0); + active_interval = timex_set(AODVV2_ACTIVE_INTERVAL, 0); + max_idletime = timex_set(AODVV2_MAX_IDLETIME, 0); + validity_t = timex_set(AODVV2_ACTIVE_INTERVAL + AODVV2_MAX_IDLETIME, 0); + + memset(&routing_table, 0, sizeof(routing_table)); + AODV_DEBUG("routing table initialized.\n"); +} + +struct netaddr *routingtable_get_next_hop(struct netaddr *dest, aodvv2_metric_t metricType) +{ + struct aodvv2_routing_entry_t *entry = routingtable_get_entry(dest, metricType); + if (!entry) { + return NULL; + } + return (&entry->nextHopAddr); +} + +void routingtable_add_entry(struct aodvv2_routing_entry_t *entry) +{ + /* only add if we don't already know the address */ + if (routingtable_get_entry(&(entry->addr), entry->metricType)) { + return; + } + /*find free spot in RT and place rt_entry there */ + for (unsigned i = 0; i < AODVV2_MAX_ROUTING_ENTRIES; i++) { + if (routing_table[i].addr._type == AF_UNSPEC) { + memcpy(&routing_table[i], entry, sizeof(struct aodvv2_routing_entry_t)); + return; + } + } +} + +struct aodvv2_routing_entry_t *routingtable_get_entry(struct netaddr *addr, + aodvv2_metric_t metricType) +{ + for (unsigned i = 0; i < AODVV2_MAX_ROUTING_ENTRIES; i++) { + _reset_entry_if_stale(i); + + if (!netaddr_cmp(&routing_table[i].addr, addr) + && routing_table[i].metricType == metricType) { + DEBUG("[routing] found entry for %s :", netaddr_to_string(&nbuf, addr)); +#ifdef DEBUG_ENABLED + print_routingtable_entry(&routing_table[i]); +#endif + return &routing_table[i]; + } + } + return NULL; +} + +void routingtable_delete_entry(struct netaddr *addr, aodvv2_metric_t metricType) +{ + for (unsigned i = 0; i < AODVV2_MAX_ROUTING_ENTRIES; i++) { + _reset_entry_if_stale(i); + + if (!netaddr_cmp(&routing_table[i].addr, addr) + && routing_table[i].metricType == metricType) { + memset(&routing_table[i], 0, sizeof(routing_table[i])); + return; + } + } +} + +void routingtable_break_and_get_all_hopping_over(struct netaddr *hop, + struct unreachable_node unreachable_nodes[], + size_t *len) +{ + *len = 0; /* to be sure */ + + for (unsigned i = 0; i < AODVV2_MAX_ROUTING_ENTRIES; i++) { + _reset_entry_if_stale(i); + + if (netaddr_cmp(&routing_table[i].nextHopAddr, hop) == 0) { + if (routing_table[i].state == ROUTE_STATE_ACTIVE && + *len < AODVV2_MAX_UNREACHABLE_NODES) { + /* when the max number of unreachable nodes is reached we're screwed. + * the above check is just damage control. */ + unreachable_nodes[*len].addr = routing_table[i].addr; + unreachable_nodes[*len].seqnum = routing_table[i].seqnum; + + (*len)++; + DEBUG("\t[routing] unreachable node found: %s\n", netaddr_to_string(&nbuf, &routing_table[i].nextHopAddr)); + } + routing_table[i].state = ROUTE_STATE_BROKEN; + DEBUG("\t[routing] number of unreachable nodes: %i\n", *len); + } + } +} + +/* + * Check if entry at index i is stale as described in Section 6.3. + * and clear the struct it fills if it is + */ +static void _reset_entry_if_stale(uint8_t i) +{ + vtimer_now(&now); + timex_t lastUsed, expirationTime; + + if (timex_cmp(routing_table[i].expirationTime, null_time) == 0) { + return; + } + + int state = routing_table[i].state; + lastUsed = routing_table[i].lastUsed; + expirationTime = routing_table[i].expirationTime; + + /* an Active route is considered to remain Active as long as it is used at least once + * during every ACTIVE_INTERVAL. When a route is no longer Active, it becomes an Idle route. */ + + /* if the node is younger than the active interval, don't bother */ + if (timex_cmp(now, active_interval) < 0) { + return; + } + + if ((state == ROUTE_STATE_ACTIVE) && + (timex_cmp(timex_sub(now, active_interval), lastUsed) == 1)) { + DEBUG("\t[routing] route towards %s Idle\n", + netaddr_to_string(&nbuf, &routing_table[i].addr)); + routing_table[i].state = ROUTE_STATE_IDLE; + routing_table[i].lastUsed = now; /* mark the time entry was set to Idle */ + } + + /* After an idle route remains Idle for MAX_IDLETIME, it becomes an Expired route. + A route MUST be considered Expired if Current_Time >= Route.ExpirationTime + */ + + /* if the node is younger than the expiration time, don't bother */ + if (timex_cmp(now, expirationTime) < 0) { + return; + } + + if ((state == ROUTE_STATE_IDLE) && + (timex_cmp(expirationTime, now) < 1)) { + DEBUG("\t[routing] route towards %s Expired\n", + netaddr_to_string(&nbuf, &routing_table[i].addr)); + DEBUG("\t expirationTime: %"PRIu32":%"PRIu32" , now: %"PRIu32":%"PRIu32"\n", + expirationTime.seconds, expirationTime.microseconds, + now.seconds, now.microseconds); + routing_table[i].state = ROUTE_STATE_EXPIRED; + routing_table[i].lastUsed = now; /* mark the time entry was set to Expired */ + } + + /* After that time, old sequence number information is considered no longer + * valuable and the Expired route MUST BE expunged */ + if (timex_cmp(timex_sub(now, lastUsed), max_seqnum_lifetime) >= 0) { + DEBUG("\t[routing] reset routing table entry for %s at %i\n", + netaddr_to_string(&nbuf, &routing_table[i].addr), i); + memset(&routing_table[i], 0, sizeof(routing_table[i])); + } +} + +bool routingtable_offers_improvement(struct aodvv2_routing_entry_t *rt_entry, + struct node_data *node_data) +{ + /* Check if new info is stale */ + if (seqnum_cmp(node_data->seqnum, rt_entry->seqnum) == -1) { + return false; + } + /* Check if new info is more costly */ + if ((node_data->metric >= rt_entry->metric) + && !(rt_entry->state != ROUTE_STATE_BROKEN)) { + return false; + } + /* Check if new info repairs a broken route */ + if (!(rt_entry->state != ROUTE_STATE_BROKEN)) { + return false; + } + return true; +} + +void routingtable_fill_routing_entry_t_rreq(struct aodvv2_packet_data *packet_data, + struct aodvv2_routing_entry_t *rt_entry, + uint8_t link_cost) +{ + rt_entry->addr = packet_data->origNode.addr; + rt_entry->seqnum = packet_data->origNode.seqnum; + rt_entry->nextHopAddr = packet_data->sender; + rt_entry->lastUsed = packet_data->timestamp; + rt_entry->expirationTime = timex_add(packet_data->timestamp, validity_t); + rt_entry->metricType = packet_data->metricType; + rt_entry->metric = packet_data->origNode.metric + link_cost; + rt_entry->state = ROUTE_STATE_ACTIVE; +} + +void routingtable_fill_routing_entry_t_rrep(struct aodvv2_packet_data *packet_data, + struct aodvv2_routing_entry_t *rt_entry, + uint8_t link_cost) +{ + rt_entry->addr = packet_data->targNode.addr; + rt_entry->seqnum = packet_data->targNode.seqnum; + rt_entry->nextHopAddr = packet_data->sender; + rt_entry->lastUsed = packet_data->timestamp; + rt_entry->expirationTime = timex_add(packet_data->timestamp, validity_t); + rt_entry->metricType = packet_data->metricType; + rt_entry->metric = packet_data->targNode.metric + link_cost; + rt_entry->state = ROUTE_STATE_ACTIVE; +} + +void print_routingtable(void) +{ + printf("===== BEGIN ROUTING TABLE ===================\n"); + for (int i = 0; i < AODVV2_MAX_ROUTING_ENTRIES; i++) { + /* route has been used before => non-empty entry */ + if (routing_table[i].lastUsed.seconds + || routing_table[i].lastUsed.microseconds) { + print_routingtable_entry(&routing_table[i]); + } + } + printf("===== END ROUTING TABLE =====================\n"); +} + +void print_routingtable_entry(struct aodvv2_routing_entry_t *rt_entry) +{ + struct netaddr_str nbuf; + + printf(".................................\n"); + printf("\t address: %s\n", netaddr_to_string(&nbuf, &(rt_entry->addr))); + printf("\t seqnum: %i\n", rt_entry->seqnum); + printf("\t nextHopAddress: %s\n", + netaddr_to_string(&nbuf, &(rt_entry->nextHopAddr))); + printf("\t lastUsed: %"PRIu32":%"PRIu32"\n", + rt_entry->lastUsed.seconds, rt_entry->lastUsed.microseconds); + printf("\t expirationTime: %"PRIu32":%"PRIu32"\n", + rt_entry->expirationTime.seconds, rt_entry->expirationTime.microseconds); + printf("\t metricType: %i\n", rt_entry->metricType); + printf("\t metric: %d\n", rt_entry->metric); + printf("\t state: %d\n", rt_entry->state); +} diff --git a/sys/net/routing/aodvv2/routingtable.h b/sys/net/routing/aodvv2/routingtable.h new file mode 100644 index 000000000000..5e1e533aaac4 --- /dev/null +++ b/sys/net/routing/aodvv2/routingtable.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014 Lotte Steenbrink + * + * 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 aodvv2 + * @{ + * + * @file routingtable.h + * @brief Cobbled-together routing table. + * + * @author Lotte Steenbrink + */ + +#ifndef AODVV2_ROUTINGTABLE_H_ +#define AODVV2_ROUTINGTABLE_H_ + +#include + +#include "common/netaddr.h" + +#include "aodvv2/types.h" +#include "constants.h" +#include "seqnum.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A route table entry (i.e., a route) may be in one of the following states: + */ +enum aodvv2_routing_states +{ + ROUTE_STATE_ACTIVE, + ROUTE_STATE_IDLE, + ROUTE_STATE_EXPIRED, + ROUTE_STATE_BROKEN, + ROUTE_STATE_TIMED +}; + +/** + * all fields of a routing table entry + */ +struct aodvv2_routing_entry_t +{ + struct netaddr addr; /**< IP address of this route's destination */ + aodvv2_seqnum_t seqnum; /**< The Sequence Number obtained from the + * last packet that updated the entry */ + struct netaddr nextHopAddr; /**< IP address of the the next hop towards + * the destination */ + timex_t lastUsed; /**< IP address of this route's destination */ + timex_t expirationTime; /**< Time at which this route expires */ + aodvv2_metric_t metricType; /**< Metric type of this route */ + uint8_t metric; /**< Metric value of this route*/ + uint8_t state; /**< State of this route + * (i.e. one of aodvv2_routing_states) */ +}; + +/** + * @brief Initialize routing table. + */ +void routingtable_init(void); + +/** + * @brief Get next hop towards dest. + * Returns NULL if dest is not in routing table. + * + * @param[in] dest Destination of the packet + * @param[in] metricType Metric Type of the desired route + * @return next hop towards dest if it exists, NULL otherwise + */ +struct netaddr *routingtable_get_next_hop(struct netaddr *dest, aodvv2_metric_t metricType); + +/** + * @brief Add new entry to routing table, if there is no other entry + * to the same destination. + * + * @param[in] entry The routing table entry to add + */ +void routingtable_add_entry(struct aodvv2_routing_entry_t *entry); + +/** + * @brief Retrieve pointer to a routing table entry. + * To edit, simply follow the pointer. + * Returns NULL if addr is not in routing table. + * + * @param[in] addr The address towards which the route should point + * @param[in] metricType Metric Type of the desired route + * @return Routing table entry if it exists, NULL otherwise + */ +struct aodvv2_routing_entry_t *routingtable_get_entry(struct netaddr *addr, aodvv2_metric_t metricType); + +/** + * @brief Delete routing table entry towards addr with metric type MetricType, + * if it exists. + * + * @param[in] addr The address towards which the route should point + * @param[in] metricType Metric Type of the desired route + */ +void routingtable_delete_entry(struct netaddr *addr, aodvv2_metric_t metricType); + +/** + * Find all routing table entries that use hop as their nextHopAddress, mark them + * as broken, write the active one into unreachable_nodes[] and increment len + * accordingly. (Sorry about the Name.) + * + * @param hop Address of the newly unreachable next hop + * @param unreachable_nodes[] array of newlu unreachable nodes to be filled. + * should be empty. + * @param len size_t* which will contain the length of + * unreachable_nodes[] after execution + */ +void routingtable_break_and_get_all_hopping_over(struct netaddr *hop, + struct unreachable_node unreachable_nodes[], + size_t *len); + +/** + * Check if the data of a RREQ or RREP offers improvement for an existing routing + * table entry. + * @param rt_entry the routing table entry to check + * @param node_data The data to check against. When handling a RREQ, + * the OrigNode's information (i.e. packet_data.origNode) + * must be passed. When handling a RREP, the + * TargNode's data (i.e. packet_data.targNode) must + * be passed. + */ +bool routingtable_offers_improvement(struct aodvv2_routing_entry_t *rt_entry, + struct node_data *node_data); + +/** + * Fills a routing table entry with the data of a RREQ. + * @param packet_data the RREQ's data + * @param rt_entry the routing table entry to fill + * @param link_cost the link cost for this RREQ + */ +void routingtable_fill_routing_entry_t_rreq(struct aodvv2_packet_data *packet_data, + struct aodvv2_routing_entry_t *rt_entry, + uint8_t link_cost); + +/** + * Fills a routing table entry with the data of a RREP. + * @param packet_data the RREP's data + * @param rt_entry the routing table entry to fill + * @param link_cost the link cost for this RREP + */ +void routingtable_fill_routing_entry_t_rrep(struct aodvv2_packet_data *packet_data, + struct aodvv2_routing_entry_t *rt_entry, + uint8_t link_cost); + +void print_routingtable(void); +void print_routingtable_entry(struct aodvv2_routing_entry_t *rt_entry); + +#ifdef __cplusplus +} +#endif + +#endif /* AODVV2_ROUTINGTABLE_H_*/ diff --git a/sys/net/routing/aodvv2/seqnum.c b/sys/net/routing/aodvv2/seqnum.c new file mode 100644 index 000000000000..d6da9ae314af --- /dev/null +++ b/sys/net/routing/aodvv2/seqnum.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014 Lotte Steenbrink + * + * 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 aodvv2 + * @{ + * + * @file seqnum.c + * @brief aodvv2 sequence number + * + * @author Lotte Steenbrink + */ + +#include "debug.h" + +#include "seqnum.h" + +#define ENABLE_DEBUG (0) + +static aodvv2_seqnum_t seqnum; + +void seqnum_init(void) +{ + seqnum = 1; +} + +void seqnum_inc(void) +{ + if (seqnum == 65535) { + seqnum = 1; + } + else if (seqnum == 0) { + DEBUG("ERROR: SeqNum shouldn't be 0! \n"); /* TODO handle properly */ + } + else { + seqnum++; + } +} + +aodvv2_seqnum_t seqnum_get(void) +{ + return seqnum; +} diff --git a/sys/net/routing/aodvv2/seqnum.h b/sys/net/routing/aodvv2/seqnum.h new file mode 100644 index 000000000000..0a11a44b6c05 --- /dev/null +++ b/sys/net/routing/aodvv2/seqnum.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014 Lotte Steenbrink + * + * 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 aodvv2 + * @{ + * + * @file seqnum.h + * @brief aodvv2 sequence number + * + * @author Lotte Steenbrink + */ + +#ifndef AODVV2_SEQNUM_H_ +#define AODVV2_SEQNUM_H_ + +#include + +#include "aodvv2/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize sequence number. + */ +void seqnum_init(void); + +/** + * @brief Get sequence number. + * + * @return sequence number + */ +aodvv2_seqnum_t seqnum_get(void); + +/** + * @brief Increment the sequence number by 1. + */ +void seqnum_inc(void); + +/** + * @brief Compare 2 sequence numbers. + * @param[in] s1 first sequence number + * @param[in] s2 second sequence number + * @return -1 when s1 is smaller, 0 if equal, 1 if s1 is bigger. + */ +static inline int seqnum_cmp(aodvv2_seqnum_t s1, aodvv2_seqnum_t s2) +{ + return s1 == s2 ? 0 : (s1 > s2 ? +1 : -1); +} + +#ifdef __cplusplus +} +#endif + +#endif /* AODVV2_SEQNUM_H_ */ diff --git a/sys/net/routing/aodvv2/utils.c b/sys/net/routing/aodvv2/utils.c new file mode 100644 index 000000000000..de48d134427d --- /dev/null +++ b/sys/net/routing/aodvv2/utils.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014 Lotte Steenbrink + * + * 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 aodvv2 + * @{ + * + * @file utils.c + * @brief client- and RREQ-table, ipv6 address representation converters + * + * @author Lotte Steenbrink + */ + +#include "utils.h" +#include "debug.h" + +#include "aodv_debug.h" + +#define ENABLE_DEBUG (0) + +/* Some aodvv2 utilities (mostly tables) */ +static mutex_t clientt_mutex; +static mutex_t rreqt_mutex; + +/* helper functions */ +static struct aodvv2_rreq_entry *_get_comparable_rreq(struct aodvv2_packet_data *packet_data); +static void _add_rreq(struct aodvv2_packet_data *packet_data); +static void _reset_entry_if_stale(uint8_t i); + +static struct netaddr client_table[AODVV2_MAX_CLIENTS]; +static struct aodvv2_rreq_entry rreq_table[AODVV2_RREQ_BUF]; + +#ifdef DEBUG_ENABLED +static struct netaddr_str nbuf; +#endif +static timex_t null_time, now, _max_idletime; + + +void clienttable_init(void) +{ + mutex_lock(&clientt_mutex); + memset(&client_table, 0, sizeof(client_table)); + mutex_unlock(&clientt_mutex); + + AODV_DEBUG("client table initialized.\n"); +} + +void clienttable_add_client(struct netaddr *addr) +{ + if (clienttable_is_client(addr)){ + return; + } + + /*find free spot in client table and place client address there */ + mutex_lock(&clientt_mutex); + for (unsigned i = 0; i < AODVV2_MAX_CLIENTS; i++) { + if ((client_table[i]._type == AF_UNSPEC) && + (client_table[i]._prefix_len == 0)) { + client_table[i] = *addr; + AODV_DEBUG("clienttable: added client %s\n", + netaddr_to_string(&nbuf, addr)); + mutex_unlock(&clientt_mutex); + return; + } + } + AODV_DEBUG("Error: Client could not be added: Client table is full.\n"); + mutex_unlock(&clientt_mutex); +} + +bool clienttable_is_client(struct netaddr *addr) +{ + mutex_lock(&clientt_mutex); + for (unsigned i = 0; i < AODVV2_MAX_CLIENTS; i++) { + if (!netaddr_cmp(&client_table[i], addr)) { + mutex_unlock(&clientt_mutex); + return true; + } + } + mutex_unlock(&clientt_mutex); + return false; +} + +void clienttable_delete_client(struct netaddr *addr) +{ + if (!clienttable_is_client(addr)) { + return; + } + + mutex_lock(&clientt_mutex); + for (unsigned i = 0; i < AODVV2_MAX_CLIENTS; i++) { + if (!netaddr_cmp(&client_table[i], addr)) { + memset(&client_table[i], 0, sizeof(client_table[i])); + mutex_unlock(&clientt_mutex); + return; + } + } +} + +void rreqtable_init(void) +{ + mutex_lock(&rreqt_mutex); + null_time = timex_set(0, 0); + _max_idletime = timex_set(AODVV2_MAX_IDLETIME, 0); + + memset(&rreq_table, 0, sizeof(rreq_table)); + mutex_unlock(&rreqt_mutex); + AODV_DEBUG("RREQ table initialized.\n"); +} + +bool rreqtable_is_redundant(struct aodvv2_packet_data *packet_data) +{ + struct aodvv2_rreq_entry *comparable_rreq; + timex_t now; + bool result; + + mutex_lock(&rreqt_mutex); + comparable_rreq = _get_comparable_rreq(packet_data); + + /* if there is no comparable rreq stored, add one and return false */ + if (comparable_rreq == NULL) { + _add_rreq(packet_data); + result = false; + } + else { + int seqnum_comparison = seqnum_cmp(packet_data->origNode.seqnum, comparable_rreq->seqnum); + + /* + * If two RREQs have the same + * metric type and OrigNode and Targnode addresses, the information from + * the one with the older Sequence Number is not needed in the table + */ + if (seqnum_comparison == -1) { + result = true; + } + + if (seqnum_comparison == 1) { + /* Update RREQ table entry with new seqnum value */ + comparable_rreq->seqnum = packet_data->origNode.seqnum; + } + + /* + * in case they have the same Sequence Number, the one with the greater + * Metric value is not needed + */ + if (seqnum_comparison == 0) { + if (comparable_rreq->metric <= packet_data->origNode.metric) { + result = true; + } + /* Update RREQ table entry with new metric value */ + comparable_rreq->metric = packet_data->origNode.metric; + } + + /* Since we've changed RREQ info, update the timestamp */ + vtimer_now(&now); + comparable_rreq->timestamp = now; + result = true; + } + + mutex_unlock(&rreqt_mutex); + return result; +} + +/* + * retrieve pointer to a comparable (according to Section 6.7.) + * RREQ table entry if it exists and NULL otherwise. + * Two AODVv2 RREQ messages are comparable if: + * - they have the same metric type + * - they have the same OrigNode and TargNode addresses + */ +static struct aodvv2_rreq_entry *_get_comparable_rreq(struct aodvv2_packet_data *packet_data) +{ + for (unsigned i = 0; i < AODVV2_RREQ_BUF; i++) { + _reset_entry_if_stale(i); + + if (!netaddr_cmp(&rreq_table[i].origNode, &packet_data->origNode.addr) + && !netaddr_cmp(&rreq_table[i].targNode, &packet_data->targNode.addr) + && rreq_table[i].metricType == packet_data->metricType) { + return &rreq_table[i]; + } + } + + return NULL; +} + + +static void _add_rreq(struct aodvv2_packet_data *packet_data) +{ + if (_get_comparable_rreq(packet_data)) { + return; + } + /*find empty rreq and fill it with packet_data */ + + for (unsigned i = 0; i < AODVV2_RREQ_BUF; i++) { + if (!rreq_table[i].timestamp.seconds && + !rreq_table[i].timestamp.microseconds) { + rreq_table[i].origNode = packet_data->origNode.addr; + rreq_table[i].targNode = packet_data->targNode.addr; + rreq_table[i].metricType = packet_data->metricType; + rreq_table[i].metric = packet_data->origNode.metric; + rreq_table[i].seqnum = packet_data->origNode.seqnum; + rreq_table[i].timestamp = packet_data->timestamp; + return; + } + } +} + +/* + * Check if entry at index i is stale and clear the struct it fills if it is + */ +static void _reset_entry_if_stale(uint8_t i) +{ + vtimer_now(&now); + + if (timex_cmp(rreq_table[i].timestamp, null_time) == 0) { + return; + } + timex_t expiration_time = timex_add(rreq_table[i].timestamp, _max_idletime); + if (timex_cmp(expiration_time, now) < 0) { + /* timestamp+expiration time is in the past: this entry is stale */ + DEBUG("\treset rreq table entry %s\n", + netaddr_to_string(&nbuf, &rreq_table[i].origNode)); + memset(&rreq_table[i], 0, sizeof(rreq_table[i])); + } +} + +void ipv6_addr_t_to_netaddr(ipv6_addr_t *src, struct netaddr *dst) +{ + dst->_type = AF_INET6; + dst->_prefix_len = AODVV2_RIOT_PREFIXLEN; + memcpy(dst->_addr, src, sizeof(dst->_addr)); +} + +void netaddr_to_ipv6_addr_t(struct netaddr *src, ipv6_addr_t *dst) +{ + memcpy(dst, src->_addr, sizeof(uint8_t) * NETADDR_MAX_LENGTH); +} diff --git a/sys/net/routing/aodvv2/utils.h b/sys/net/routing/aodvv2/utils.h new file mode 100644 index 000000000000..8baee0e1f98d --- /dev/null +++ b/sys/net/routing/aodvv2/utils.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014 Lotte Steenbrink + * + * 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 aodvv2 + * @{ + * + * @file utils.h + * @brief client- and RREQ-table, ipv6 address representation converters + * + * @author Lotte Steenbrink + */ + +#ifndef AODVV2_UTILS_H_ +#define AODVV2_UTILS_H_ + +#include + +#include "ipv6.h" + +#include "common/netaddr.h" + +#include "aodvv2/types.h" +#include "constants.h" +#include "seqnum.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define AODVV2_MAX_CLIENTS 1 /** multiple clients are currently not supported. */ +#define AODVV2_RREQ_BUF 128 /** should be enough for now... */ +#define AODVV2_RREQ_WAIT_TIME 2 /** seconds */ +#define AODVV2_RIOT_PREFIXLEN 128 /** Prefix length of the IPv6 addresses + * used in the network served by AODVv2 () */ +/** + * @brief RREQ Table entry which stores all information about a RREQ that was received + * in order to avoid duplicates. + */ +struct aodvv2_rreq_entry +{ + struct netaddr origNode; /**< Node which originated the RREQ*/ + struct netaddr targNode; /**< Target (destination) of the RREQ */ + aodvv2_metric_t metricType; /**< Metric type of the RREQ */ + uint8_t metric; /**< Metric of the RREQ */ + aodvv2_seqnum_t seqnum; /**< Sequence number of the RREQ */ + timex_t timestamp; /**< Last time this entry was updated */ +}; + +/** + * Initialize table of clients that the router currently serves. + */ +void clienttable_init(void); + +/** + * Add client to the list of clients that the router currently serves. + * @param addr address of the client + * (Since the current version doesn't offer support for + * Client Networks, the prefixlen is currently ignored.) + */ +void clienttable_add_client(struct netaddr *addr); + +/** + * Find out if a client is in the list of clients that the router currently serves. + * @param addr address of the client in question + * (Since the current version doesn't offer support for + * Client Networks, the prefixlen is currently ignored.) + */ +bool clienttable_is_client(struct netaddr *addr); + +/** + * Delete a client from the list of clients that the router currently serves. + * @param addr address of the client to delete + * (Since the current version doesn't offer support for + * Client Networks, the prefixlen is currently ignored.) + */ +void clienttable_delete_client(struct netaddr *addr); + +/** + * Initialize RREQ table. + */ +void rreqtable_init(void); + +/** + * Check if a RREQ is redundant, i.e. was received from another node already. + * Behaves as described in Sections 5.7. and 7.6. + * @param packet_data data of the RREQ in question + */ +bool rreqtable_is_redundant(struct aodvv2_packet_data *packet_data); + +/** + * Convert an IP stored as an ipv6_addr_t to a netaddr + * @param src ipv6_addr_t to convert + * @param dst (empty) netaddr to convert into + */ +void ipv6_addr_t_to_netaddr(ipv6_addr_t *src, struct netaddr *dst); + +/** + * Convert an IP stored as a netaddr to an ipv6_addr_t + * @param src (empty) netaddr to convert into + * @param dst ipv6_addr_t to convert + */ +void netaddr_to_ipv6_addr_t(struct netaddr *src, ipv6_addr_t *dst); + +#ifdef __cplusplus +} +#endif + +#endif /* AODVV2_UTILS_H_ */ diff --git a/sys/net/routing/aodvv2/writer.c b/sys/net/routing/aodvv2/writer.c new file mode 100644 index 000000000000..79acc8e024af --- /dev/null +++ b/sys/net/routing/aodvv2/writer.c @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014 Lotte Steenbrink + * + * 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 aodvv2 + * @{ + * + * @file writer.c + * @brief writer to create RFC5444 aodvv2 messages + * + * @author Lotte Steenbrink + */ + +#ifdef RIOT +#include "net_help.h" +#endif + +#include "writer.h" +#include "debug.h" + +#include "aodv_debug.h" + +#define ENABLE_DEBUG (0) + +static void _cb_addMessageHeader(struct rfc5444_writer *wr, + struct rfc5444_writer_message *message); + +static void _cb_rreq_addAddresses(struct rfc5444_writer *wr); +static void _cb_rrep_addAddresses(struct rfc5444_writer *wr); +static void _cb_rerr_addAddresses(struct rfc5444_writer *wr); + +static mutex_t writer_mutex; + +struct rfc5444_writer writer; +static struct writer_target _target; + +static struct unreachable_node *_unreachable_nodes; +static size_t _num_unreachable_nodes; + +static uint8_t _msg_buffer[128]; +static uint8_t _msg_addrtlvs[1000]; +static uint8_t _packet_buffer[128]; + +static struct rfc5444_writer_message *_rreq_msg; +static struct rfc5444_writer_message *_rrep_msg; +static struct rfc5444_writer_message *_rerr_msg; + +/* + * message content provider that will add message TLVs, + * addresses and address block TLVs to all messages of type RREQ. + */ +static struct rfc5444_writer_content_provider _rreq_message_content_provider = +{ + .msg_type = RFC5444_MSGTYPE_RREQ, + .addAddresses = _cb_rreq_addAddresses, +}; + +/* declaration of all address TLVs added to the RREQ message */ +static struct rfc5444_writer_tlvtype _rreq_addrtlvs[] = +{ + [RFC5444_MSGTLV_ORIGSEQNUM] = { .type = RFC5444_MSGTLV_ORIGSEQNUM }, + [RFC5444_MSGTLV_METRIC] = { + .type = RFC5444_MSGTLV_METRIC, + .exttype = AODVV2_DEFAULT_METRIC_TYPE + }, +}; + +/* + * message content provider that will add message TLVs, + * addresses and address block TLVs to all messages of type RREQ. + */ +static struct rfc5444_writer_content_provider _rrep_message_content_provider = +{ + .msg_type = RFC5444_MSGTYPE_RREP, + .addAddresses = _cb_rrep_addAddresses, +}; + +/* declaration of all address TLVs added to the RREP message */ +static struct rfc5444_writer_tlvtype _rrep_addrtlvs[] = +{ + [RFC5444_MSGTLV_ORIGSEQNUM] = { .type = RFC5444_MSGTLV_ORIGSEQNUM}, + [RFC5444_MSGTLV_TARGSEQNUM] = { .type = RFC5444_MSGTLV_TARGSEQNUM}, + [RFC5444_MSGTLV_METRIC] = { + .type = RFC5444_MSGTLV_METRIC, + .exttype = AODVV2_DEFAULT_METRIC_TYPE + }, +}; + +/* + * message content provider that will add message TLVs, + * addresses and address block TLVs to all messages of type RREQ. + */ +static struct rfc5444_writer_content_provider _rerr_message_content_provider = +{ + .msg_type = RFC5444_MSGTYPE_RERR, + .addAddresses = _cb_rerr_addAddresses, +}; + +/* declaration of all address TLVs added to the RREP message */ +static struct rfc5444_writer_tlvtype _rerr_addrtlvs[] = +{ + [RFC5444_MSGTLV_UNREACHABLE_NODE_SEQNUM] = { .type = RFC5444_MSGTLV_UNREACHABLE_NODE_SEQNUM}, +}; + +/** + * Callback to define the message header for a RFC5444 RREQ message + * @param message + */ +static void +_cb_addMessageHeader(struct rfc5444_writer *wr, struct rfc5444_writer_message *message) +{ + AODV_DEBUG("%s()\n", __func__); + + /* no originator, no hopcount, has hoplimit, no seqno */ + rfc5444_writer_set_msg_header(wr, message, false, false, true, false); + rfc5444_writer_set_msg_hoplimit(wr, message, _target.packet_data.hoplimit); +} + +/** + * Callback to add addresses and address TLVs to a RFC5444 RREQ message + */ +static void +_cb_rreq_addAddresses(struct rfc5444_writer *wr) +{ + AODV_DEBUG("%s()\n", __func__); + + struct rfc5444_writer_address *origNode_addr; + + /* add origNode address (has no address tlv); is mandatory address */ + origNode_addr = rfc5444_writer_add_address(wr, _rreq_message_content_provider.creator, + &_target.packet_data.origNode.addr, true); + + /* add targNode address (has no address tlv); is mandatory address */ + rfc5444_writer_add_address(wr, _rreq_message_content_provider.creator, + &_target.packet_data.targNode.addr, true); + + /* add SeqNum TLV and metric TLV to origNode */ + /* TODO: allow_dup true or false? */ + rfc5444_writer_add_addrtlv(wr, origNode_addr, &_rreq_addrtlvs[RFC5444_MSGTLV_ORIGSEQNUM], + &_target.packet_data.origNode.seqnum, + sizeof(_target.packet_data.origNode.seqnum), false); + /* cppcheck: suppress false positive on non-trivially initialized arrays. + * this is a known bug: http://trac.cppcheck.net/ticket/5497 */ + /* cppcheck-suppress arrayIndexOutOfBounds */ + rfc5444_writer_add_addrtlv(wr, origNode_addr, &_rreq_addrtlvs[RFC5444_MSGTLV_METRIC], + &_target.packet_data.origNode.metric, + sizeof(_target.packet_data.origNode.metric), false); +} + +/** + * Callback to add addresses and address TLVs to a RFC5444 RREQ message + */ +static void +_cb_rrep_addAddresses(struct rfc5444_writer *wr) +{ + AODV_DEBUG("%s()\n", __func__); + + struct rfc5444_writer_address *origNode_addr, *targNode_addr; + + uint16_t origNode_seqnum = _target.packet_data.origNode.seqnum; + + uint16_t targNode_seqnum = seqnum_get(); + seqnum_inc(); + + uint8_t targNode_hopCt = _target.packet_data.targNode.metric; + + /* add origNode address (has no address tlv); is mandatory address */ + origNode_addr = rfc5444_writer_add_address(wr, _rrep_message_content_provider.creator, + &_target.packet_data.origNode.addr, true); + + /* add targNode address (has no address tlv); is mandatory address */ + targNode_addr = rfc5444_writer_add_address(wr, _rrep_message_content_provider.creator, + &_target.packet_data.targNode.addr, true); + + /* add OrigNode and TargNode SeqNum TLVs */ + /* TODO: allow_dup true or false? */ + rfc5444_writer_add_addrtlv(wr, origNode_addr, &_rrep_addrtlvs[RFC5444_MSGTLV_ORIGSEQNUM], + &origNode_seqnum, sizeof(origNode_seqnum), false); + rfc5444_writer_add_addrtlv(wr, targNode_addr, &_rrep_addrtlvs[RFC5444_MSGTLV_TARGSEQNUM], + &targNode_seqnum, sizeof(targNode_seqnum), false); + + /* Add Metric TLV to targNode Address */ + rfc5444_writer_add_addrtlv(wr, targNode_addr, &_rrep_addrtlvs[RFC5444_MSGTLV_METRIC], + &targNode_hopCt, sizeof(targNode_hopCt), false); +} + +/** + * Callback to add addresses and address TLVs to a RFC5444 RERR message + */ +static void +_cb_rerr_addAddresses(struct rfc5444_writer *wr) +{ + AODV_DEBUG("%s()\n", __func__); + + for (unsigned i = 0; i < _num_unreachable_nodes; i++) { + /* add unreachableNode addresses (has no address tlv); is mandatory address */ + struct rfc5444_writer_address *unreachableNode_addr = rfc5444_writer_add_address( + wr, _rerr_message_content_provider.creator, + &_unreachable_nodes[i].addr, true); + + /* add SeqNum TLV to unreachableNode */ + /* TODO: allow_dup true or false? */ + /* cppcheck: suppress false positive on non-trivially initialized arrays. + * this is a known bug: http://trac.cppcheck.net/ticket/5497 */ + /* cppcheck-suppress arrayIndexOutOfBounds */ + rfc5444_writer_add_addrtlv(wr, unreachableNode_addr, + &_rerr_addrtlvs[RFC5444_MSGTLV_UNREACHABLE_NODE_SEQNUM], + &_unreachable_nodes[i].seqnum, + sizeof(_unreachable_nodes[i].seqnum), false); + } +} + +void aodv_packet_writer_init(write_packet_func_ptr ptr) +{ + AODV_DEBUG("%s()\n", __func__); + + mutex_init(&writer_mutex); + + /* define interface for generating rfc5444 packets */ + _target.interface.packet_buffer = _packet_buffer; + _target.interface.packet_size = sizeof(_packet_buffer); + + /* set function to send binary packet content */ + _target.interface.sendPacket = ptr; + + /* define the rfc5444 writer */ + writer.msg_buffer = _msg_buffer; + writer.msg_size = sizeof(_msg_buffer); + writer.addrtlv_buffer = _msg_addrtlvs; + writer.addrtlv_size = sizeof(_msg_addrtlvs); + + /* initialize writer */ + rfc5444_writer_init(&writer); + + /* register a target (for sending messages to) in writer */ + rfc5444_writer_register_target(&writer, &_target.interface); + + /* register a message content providers for RREQ and RREP */ + rfc5444_writer_register_msgcontentprovider(&writer, &_rreq_message_content_provider, + _rreq_addrtlvs, ARRAYSIZE(_rreq_addrtlvs)); + rfc5444_writer_register_msgcontentprovider(&writer, &_rrep_message_content_provider, + _rrep_addrtlvs, ARRAYSIZE(_rrep_addrtlvs)); + rfc5444_writer_register_msgcontentprovider(&writer, &_rerr_message_content_provider, + _rerr_addrtlvs, ARRAYSIZE(_rerr_addrtlvs)); + + /* register rreq and rrep messages with 16 byte (ipv6) addresses. + * AddPacketHeader & addMessageHeader callbacks are triggered here. */ + _rreq_msg = rfc5444_writer_register_message(&writer, RFC5444_MSGTYPE_RREQ, + false, RFC5444_MAX_ADDRLEN); + _rrep_msg = rfc5444_writer_register_message(&writer, RFC5444_MSGTYPE_RREP, + false, RFC5444_MAX_ADDRLEN); + _rerr_msg = rfc5444_writer_register_message(&writer, RFC5444_MSGTYPE_RERR, + false, RFC5444_MAX_ADDRLEN); + + _rreq_msg->addMessageHeader = _cb_addMessageHeader; + _rrep_msg->addMessageHeader = _cb_addMessageHeader; + _rerr_msg->addMessageHeader = _cb_addMessageHeader; +} + +/** + * Send a RREQ. DO NOT use this function to dispatch packets from anything else + * than the sender_thread. To send RREQs, use aodv_send_rreq(). + * @param packet_data parameters of the RREQ + * @param next_hop Address the RREP is sent to + */ +void aodv_packet_writer_send_rreq(struct aodvv2_packet_data *packet_data, struct netaddr *next_hop) +{ + AODV_DEBUG("%s()\n", __func__); + + if ((packet_data == NULL) || (next_hop == NULL)) { + return; + } + + /* Make sure no other thread is using the writer right now */ + mutex_lock(&writer_mutex); + memcpy(&_target.packet_data, packet_data, sizeof(struct aodvv2_packet_data)); + _target.type = RFC5444_MSGTYPE_RREQ; + _target.packet_data.hoplimit = packet_data->hoplimit; + + /* set address to which the write_packet callback should send our RREQ */ + memcpy(&_target.target_addr, next_hop, sizeof (struct netaddr)); + + rfc5444_writer_create_message_alltarget(&writer, RFC5444_MSGTYPE_RREQ); + rfc5444_writer_flush(&writer, &_target.interface, false); + mutex_unlock(&writer_mutex); +} + + +/** + * Send a RREP. DO NOT use this function to dispatch packets from anything else + * than the sender_thread. To send RREPs, use aodv_send_rrep(). + * @param packet_data parameters of the RREP + * @param next_hop Address the RREP is sent to + */ +void aodv_packet_writer_send_rrep(struct aodvv2_packet_data *packet_data, struct netaddr *next_hop) +{ + AODV_DEBUG("%s()\n", __func__); + + if ((packet_data == NULL) || (next_hop == NULL)) { + return; + } + + /* Make sure no other thread is using the writer right now */ + mutex_lock(&writer_mutex); + memcpy(&_target.packet_data, packet_data, sizeof(struct aodvv2_packet_data)); + _target.type = RFC5444_MSGTYPE_RREP; + _target.packet_data.hoplimit = AODVV2_MAX_HOPCOUNT; + + /* set address to which the write_packet callback should send our RREQ */ + memcpy(&_target.target_addr, next_hop, sizeof (struct netaddr)); + + rfc5444_writer_create_message_alltarget(&writer, RFC5444_MSGTYPE_RREP); + rfc5444_writer_flush(&writer, &_target.interface, false); + mutex_unlock(&writer_mutex); +} + +/** + * Send a RERR. DO NOT use this function to dispatch packets from anything else + * than the sender_thread. To send RERRs, use aodv_send_rerr(). + * @param unreachable_nodes[] array containing all newly unreachable nodes. each + * in a struct unreachable_node + * @param len length of unreachable_nodes[] + * @param hoplimit the message's hop limit + * @param next_hop Address the RREP is sent to + */ +void aodv_packet_writer_send_rerr(struct unreachable_node unreachable_nodes[], size_t len, + int hoplimit, struct netaddr *next_hop) +{ + AODV_DEBUG("%s()\n", __func__); + + if ((unreachable_nodes == NULL) || (next_hop == NULL)) { + return; + } + + mutex_lock(&writer_mutex); + _target.packet_data.hoplimit = hoplimit; + _target.type = RFC5444_MSGTYPE_RERR; + _unreachable_nodes = unreachable_nodes; + _num_unreachable_nodes = len; + + /* set address to which the write_packet callback should send our RREQ */ + memcpy(&_target.target_addr, next_hop, sizeof (struct netaddr)); + + rfc5444_writer_create_message_alltarget(&writer, RFC5444_MSGTYPE_RERR); + rfc5444_writer_flush(&writer, &_target.interface, false); + mutex_unlock(&writer_mutex); +} + +void aodv_packet_writer_cleanup(void) +{ + AODV_DEBUG("%s()\n", __func__); + rfc5444_writer_cleanup(&writer); +} diff --git a/sys/net/routing/aodvv2/writer.h b/sys/net/routing/aodvv2/writer.h new file mode 100644 index 000000000000..f3ace00c2c9b --- /dev/null +++ b/sys/net/routing/aodvv2/writer.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014 Lotte Steenbrink + * + * 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 aodvv2 + * @{ + * + * @file writer.h + * @brief writer to create RFC5444 aodvv2 messages + * + * @author Lotte Steenbrink + */ + +#ifndef AODVV2_WRITER_H_ +#define AODVV2_WRITER_H_ + +#include "common/netaddr.h" +#include "rfc5444/rfc5444_writer.h" +#include "mutex.h" + +#include "constants.h" +#include "seqnum.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Wrapper for the rfc5444_writer_target that the _write_packet() callback receives. + * _write_packet() needs to know the type, payload and target address + * of the RFC5444 message to be sent as well, but the oonf api does not + * offer this feature. Having this wrapper enables the use of the + * container_of macro to fetch this information. + * It is hacky, but it does the trick. + */ +struct writer_target +{ + struct rfc5444_writer_target interface; /**< Interface for generating rfc5444 packets */ + struct netaddr target_addr; /**< Address to which the packet should be sent */ + struct aodvv2_packet_data packet_data; /**< Payload of the AODVv2 Message */ + int type; /**< Type of the AODVv2 Message (i.e. rfc5444_msg_type) */ +}; + +/** + * @brief oonf api voodo. Pointer to a callback function which is passed to + * writer_init() and called when the packet is ready to send. + */ +typedef void (*write_packet_func_ptr)( + struct rfc5444_writer *wr, struct rfc5444_writer_target *iface, + void *buffer, size_t length); + +/** + * @brief Initialize RFC5444 writer + * @param ptr pointer to "send_packet" callback + */ +void aodv_packet_writer_init(write_packet_func_ptr ptr); + +/** + * @brief Clean up after the RFC5444 writer + */ +void aodv_packet_writer_cleanup(void); + +/** + * @brief Send a RREQ. DO NOT use this function to dispatch packets from anything else + * than the sender_thread. To send RREQs, use aodv_send_rreq(). + * @param packet_data parameters of the RREQ + * @param next_hop Address the RREP is sent to + */ +void aodv_packet_writer_send_rreq(struct aodvv2_packet_data *packet_data, struct netaddr *next_hop); + +/** + * @brief Send a RREP. DO NOT use this function to dispatch packets from anything else + * than the sender_thread. To send RREPs, use aodv_send_rrep(). + * @param packet_data parameters of the RREP + * @param next_hop Address the RREP is sent to + */ +void aodv_packet_writer_send_rrep(struct aodvv2_packet_data *packet_data, struct netaddr *next_hop); + +/** + * @brief Send a RERR. DO NOT use this function to dispatch packets from anything else + * than the sender_thread. To send RERRs, use aodv_send_rerr(). + * @param unreachable_nodes[] array containing all newly unreachable nodes. each + * in a struct unreachable_node + * @param len length of unreachable_nodes[] + * @param hoplimit the message's hop limit + * @param next_hop Address the RREP is sent to + */ +void aodv_packet_writer_send_rerr(struct unreachable_node unreachable_nodes[], size_t len, + int hoplimit, struct netaddr *next_hop); + +#ifdef __cplusplus +} +#endif + +#endif /* AODVV2_WRITER_H_ */