mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
457 lines
15 KiB
C
457 lines
15 KiB
C
/*
|
|
* Copyright (C) 2014 Freie Universität Berlin
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup nhdp
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Reader implementation for message processing in NHDP
|
|
*
|
|
* @author Fabian Nack <nack@inf.fu-berlin.de>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include "mutex.h"
|
|
#include "utlist.h"
|
|
|
|
#include "rfc5444/rfc5444.h"
|
|
#include "rfc5444/rfc5444_iana.h"
|
|
#include "rfc5444/rfc5444_reader.h"
|
|
|
|
#include "lib_table.h"
|
|
#include "nib_table.h"
|
|
#include "iib_table.h"
|
|
#include "nhdp.h"
|
|
#include "nhdp_address.h"
|
|
#include "nhdp_reader.h"
|
|
|
|
/* Internal variables */
|
|
struct rfc5444_reader reader;
|
|
static mutex_t mtx_packet_handler = MUTEX_INIT;
|
|
|
|
static nhdp_addr_entry_t *send_addr_list_head;
|
|
static nhdp_addr_entry_t *nb_addr_list_head;
|
|
static nhdp_addr_entry_t *th_sym_addr_list_head;
|
|
static nhdp_addr_entry_t *th_rem_addr_list_head;
|
|
static nhdp_addr_entry_t *rem_addr_list_head;
|
|
|
|
static kernel_pid_t if_pid;
|
|
static uint64_t val_time;
|
|
static uint64_t int_time;
|
|
static uint8_t sym = 0;
|
|
static uint8_t lost = 0;
|
|
|
|
/* Internal function prototypes */
|
|
static enum rfc5444_result _nhdp_blocktlv_msg_cb(struct rfc5444_reader_tlvblock_context *cont);
|
|
static enum rfc5444_result _nhdp_blocktlv_address_cb(struct rfc5444_reader_tlvblock_context *cont);
|
|
static enum rfc5444_result _nhdp_msg_end_cb(struct rfc5444_reader_tlvblock_context *cont,
|
|
bool dropped);
|
|
static enum rfc5444_result check_msg_validity(struct rfc5444_reader_tlvblock_context *cont);
|
|
static enum rfc5444_result check_addr_validity(nhdp_addr_t *addr);
|
|
static nhdp_addr_t *get_nhdp_db_addr(uint8_t *addr, uint8_t prefix);
|
|
static void process_temp_tables(void);
|
|
static void cleanup_temp_addr_lists(void);
|
|
|
|
/* Array containing the processable message TLVs for HELLO messages */
|
|
static struct rfc5444_reader_tlvblock_consumer_entry _nhdp_msg_tlvs[] = {
|
|
[RFC5444_MSGTLV_INTERVAL_TIME] = { .type = RFC5444_MSGTLV_INTERVAL_TIME, .mandatory = false },
|
|
[RFC5444_MSGTLV_VALIDITY_TIME] = { .type = RFC5444_MSGTLV_VALIDITY_TIME, .mandatory = true },
|
|
};
|
|
|
|
/* Array containing the processable address TLVs for HELLO message address blocks */
|
|
static struct rfc5444_reader_tlvblock_consumer_entry _nhdp_addr_tlvs[] = {
|
|
[RFC5444_ADDRTLV_LOCAL_IF] = { .type = RFC5444_ADDRTLV_LOCAL_IF },
|
|
[RFC5444_ADDRTLV_LINK_STATUS] = { .type = RFC5444_ADDRTLV_LINK_STATUS },
|
|
[RFC5444_ADDRTLV_OTHER_NEIGHB] = { .type = RFC5444_ADDRTLV_OTHER_NEIGHB },
|
|
};
|
|
|
|
/* oonf_api message consumer used for HELLO message consumption */
|
|
static struct rfc5444_reader_tlvblock_consumer _nhdp_msg_consumer = {
|
|
.msg_id = RFC5444_MSGTYPE_HELLO,
|
|
.block_callback = _nhdp_blocktlv_msg_cb,
|
|
.end_callback = _nhdp_msg_end_cb,
|
|
};
|
|
|
|
/* oonf_api message consumer user for HELLO message address block consumption */
|
|
static struct rfc5444_reader_tlvblock_consumer _nhdp_address_consumer = {
|
|
.msg_id = RFC5444_MSGTYPE_HELLO,
|
|
.addrblock_consumer = true,
|
|
.block_callback = _nhdp_blocktlv_address_cb,
|
|
};
|
|
|
|
|
|
/*---------------------------------------------------------------------------*
|
|
* NHDP Reader API *
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
void nhdp_reader_init(void)
|
|
{
|
|
/* Reset locally created address lists */
|
|
send_addr_list_head = NULL;
|
|
nb_addr_list_head = NULL;
|
|
th_sym_addr_list_head = NULL;
|
|
th_rem_addr_list_head = NULL;
|
|
rem_addr_list_head = NULL;
|
|
|
|
/* Initialize reader */
|
|
rfc5444_reader_init(&reader);
|
|
|
|
/* Register HELLO message consumer */
|
|
rfc5444_reader_add_message_consumer(&reader, &_nhdp_msg_consumer,
|
|
_nhdp_msg_tlvs, ARRAYSIZE(_nhdp_msg_tlvs));
|
|
rfc5444_reader_add_message_consumer(&reader, &_nhdp_address_consumer,
|
|
_nhdp_addr_tlvs, ARRAYSIZE(_nhdp_addr_tlvs));
|
|
}
|
|
|
|
int nhdp_reader_handle_packet(kernel_pid_t rcvg_if_pid, void *buffer, size_t length)
|
|
{
|
|
int result;
|
|
|
|
mutex_lock(&mtx_packet_handler);
|
|
|
|
/* Store PID of interface this packet was received on */
|
|
if_pid = rcvg_if_pid;
|
|
/* Parse packet with reader */
|
|
result = rfc5444_reader_handle_packet(&reader, buffer, length);
|
|
|
|
mutex_unlock(&mtx_packet_handler);
|
|
|
|
return result;
|
|
}
|
|
|
|
void nhdp_reader_cleanup(void)
|
|
{
|
|
cleanup_temp_addr_lists();
|
|
rfc5444_reader_cleanup(&reader);
|
|
}
|
|
|
|
|
|
/*------------------------------------------------------------------------------------*/
|
|
/* Internal functions */
|
|
/*------------------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Handle one address and its corresponding TLVs
|
|
* Called by oonf_api for every included address to allow parsing
|
|
*/
|
|
static enum rfc5444_result
|
|
_nhdp_blocktlv_address_cb(struct rfc5444_reader_tlvblock_context *cont)
|
|
{
|
|
uint8_t tmp_result;
|
|
/* Create address list entry to add it later to one of the temp address lists */
|
|
nhdp_addr_entry_t *current_addr = (nhdp_addr_entry_t *) malloc(sizeof(nhdp_addr_entry_t));
|
|
|
|
if (!current_addr) {
|
|
/* Insufficient memory */
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
|
|
/* Get NHDP address for the current netaddr */
|
|
current_addr->address = get_nhdp_db_addr(&cont->addr._addr[0], cont->addr._prefix_len);
|
|
|
|
if (!current_addr->address) {
|
|
/* Insufficient memory */
|
|
free(current_addr);
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
|
|
/* Check validity of address tlvs */
|
|
if (check_addr_validity(current_addr->address) != RFC5444_OKAY) {
|
|
nhdp_free_addr_entry(current_addr);
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
|
|
/* Handle address and add it to proper temporary list */
|
|
if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LOCAL_IF].tlv) {
|
|
switch (*_nhdp_addr_tlvs[RFC5444_ADDRTLV_LOCAL_IF].tlv->single_value) {
|
|
case RFC5444_LOCALIF_THIS_IF:
|
|
LL_PREPEND(send_addr_list_head, current_addr);
|
|
/* Local IF marked addresses have to be added to two temp lists */
|
|
nhdp_addr_entry_t *sec_container =
|
|
(nhdp_addr_entry_t *) malloc(sizeof(nhdp_addr_entry_t));
|
|
|
|
if (!sec_container) {
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
|
|
/* Increment usage counter of address in central NHDP address storage */
|
|
current_addr->address->usg_count++;
|
|
sec_container->address = current_addr->address;
|
|
LL_PREPEND(nb_addr_list_head, sec_container);
|
|
break;
|
|
|
|
case RFC5444_LOCALIF_OTHER_IF:
|
|
LL_PREPEND(nb_addr_list_head, current_addr);
|
|
break;
|
|
|
|
default:
|
|
/* Wrong value, drop message */
|
|
nhdp_free_addr_entry(current_addr);
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
}
|
|
else if ((tmp_result = lib_is_reg_addr(if_pid, current_addr->address))) {
|
|
/* The address is one of our local addresses (do not add it for processing) */
|
|
if ((!sym) && (tmp_result == 1) && _nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv) {
|
|
/* If address is a local address of the receiving interface, check */
|
|
/* whether we can derive a status for this link (symmetry or lost) */
|
|
switch (*_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv->single_value) {
|
|
case RFC5444_LINKSTATUS_SYMMETRIC:
|
|
/* Fall - through */
|
|
|
|
case RFC5444_LINKSTATUS_HEARD:
|
|
sym = 1;
|
|
break;
|
|
|
|
case RFC5444_LINKSTATUS_LOST:
|
|
lost = 1;
|
|
break;
|
|
|
|
default:
|
|
/* Wrong value, drop message */
|
|
nhdp_free_addr_entry(current_addr);
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
}
|
|
|
|
/* Address is one of our own addresses, ignore it */
|
|
nhdp_free_addr_entry(current_addr);
|
|
}
|
|
else if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv) {
|
|
switch (*_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv->single_value) {
|
|
case RFC5444_LINKSTATUS_SYMMETRIC:
|
|
LL_PREPEND(th_sym_addr_list_head, current_addr);
|
|
break;
|
|
|
|
case RFC5444_LINKSTATUS_HEARD:
|
|
/* Fall-through */
|
|
|
|
case RFC5444_LINKSTATUS_LOST:
|
|
if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv
|
|
&& *_nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv->single_value
|
|
== RFC5444_OTHERNEIGHB_SYMMETRIC) {
|
|
/* Symmetric has higher priority */
|
|
LL_PREPEND(th_sym_addr_list_head, current_addr);
|
|
}
|
|
else {
|
|
LL_PREPEND(th_rem_addr_list_head, current_addr);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
/* Wrong value, drop message */
|
|
nhdp_free_addr_entry(current_addr);
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
}
|
|
else if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv) {
|
|
switch (*_nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv->single_value) {
|
|
case RFC5444_OTHERNEIGHB_SYMMETRIC:
|
|
LL_PREPEND(th_sym_addr_list_head, current_addr);
|
|
break;
|
|
|
|
case RFC5444_OTHERNEIGHB_LOST:
|
|
LL_PREPEND(th_rem_addr_list_head, current_addr);
|
|
break;
|
|
|
|
default:
|
|
/* Wrong value, drop message */
|
|
nhdp_free_addr_entry(current_addr);
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
}
|
|
else {
|
|
/* Addresses without expected TLV are ignored */
|
|
nhdp_free_addr_entry(current_addr);
|
|
return RFC5444_DROP_ADDRESS;
|
|
}
|
|
|
|
return RFC5444_OKAY;
|
|
}
|
|
|
|
/**
|
|
* Handle message TLVs of received HELLO
|
|
* Called by oonf_api to allow message TLV parsing
|
|
*/
|
|
static enum rfc5444_result
|
|
_nhdp_blocktlv_msg_cb(struct rfc5444_reader_tlvblock_context *cont)
|
|
{
|
|
/* Check whether specified message TLVs are correctly included */
|
|
if (check_msg_validity(cont) != RFC5444_OKAY) {
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
|
|
/* Validity time must be included as message tlv */
|
|
val_time = rfc5444_timetlv_decode(
|
|
*_nhdp_msg_tlvs[RFC5444_MSGTLV_VALIDITY_TIME].tlv->single_value);
|
|
|
|
/* Interval time is not mandatory as message tlv */
|
|
if (_nhdp_msg_tlvs[RFC5444_MSGTLV_INTERVAL_TIME].tlv) {
|
|
int_time = rfc5444_timetlv_decode(
|
|
*_nhdp_msg_tlvs[RFC5444_MSGTLV_INTERVAL_TIME].tlv->single_value);
|
|
}
|
|
|
|
return RFC5444_OKAY;
|
|
}
|
|
|
|
/**
|
|
* Process received addresses and clean up temporary stuff
|
|
* Called by oonf_api after message was parsed
|
|
*/
|
|
static enum rfc5444_result
|
|
_nhdp_msg_end_cb(struct rfc5444_reader_tlvblock_context *cont __attribute__((unused)),
|
|
bool dropped)
|
|
{
|
|
if (!dropped) {
|
|
/* Only process the received addresses if message was valid */
|
|
process_temp_tables();
|
|
}
|
|
|
|
/* Clean all temporary stuff */
|
|
val_time = 0ULL;
|
|
int_time = 0ULL;
|
|
sym = 0;
|
|
lost = 0;
|
|
cleanup_temp_addr_lists();
|
|
|
|
if (dropped) {
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
|
|
return RFC5444_OKAY;
|
|
}
|
|
|
|
/**
|
|
* Check validity of HELLO message header and message TLVs
|
|
*/
|
|
static enum rfc5444_result check_msg_validity(struct rfc5444_reader_tlvblock_context *cont)
|
|
{
|
|
if (cont->has_hoplimit && cont->hoplimit != 1) {
|
|
/* Hop Limit other than 1 */
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
|
|
if (cont->has_hopcount && cont->hopcount != 0) {
|
|
/* Hop Count other than zero */
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
|
|
if (!(_nhdp_msg_tlvs[RFC5444_MSGTLV_VALIDITY_TIME].tlv)) {
|
|
/* No validity time tlv */
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
else if (_nhdp_msg_tlvs[RFC5444_MSGTLV_VALIDITY_TIME].tlv->next_entry) {
|
|
/* Multiple validity time tlvs */
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
|
|
if (_nhdp_msg_tlvs[RFC5444_MSGTLV_INTERVAL_TIME].tlv
|
|
&& _nhdp_msg_tlvs[RFC5444_MSGTLV_INTERVAL_TIME].tlv->next_entry) {
|
|
/* Multiple interval time tlvs */
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
|
|
return RFC5444_OKAY;
|
|
}
|
|
|
|
/**
|
|
* Check validity of address block TLVs
|
|
*/
|
|
static enum rfc5444_result check_addr_validity(nhdp_addr_t *addr)
|
|
{
|
|
if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LOCAL_IF].tlv) {
|
|
if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv
|
|
|| _nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv) {
|
|
/* Conflicting tlv types for the address */
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
else if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LOCAL_IF].tlv->next_entry) {
|
|
/* Multiple tlvs of the same type are not allowed */
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
else if (lib_is_reg_addr(if_pid, addr)) {
|
|
/* Address of one of neighbor's IFs equals one of ours */
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
}
|
|
else if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv
|
|
&& _nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_STATUS].tlv->next_entry) {
|
|
/* Multiple tlvs of the same type are not allowed */
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
else if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv
|
|
&& _nhdp_addr_tlvs[RFC5444_ADDRTLV_OTHER_NEIGHB].tlv->next_entry) {
|
|
/* Multiple tlvs of the same type are not allowed */
|
|
return RFC5444_DROP_MESSAGE;
|
|
}
|
|
|
|
return RFC5444_OKAY;
|
|
}
|
|
|
|
/**
|
|
* Get a new or existing NHDP address entry from the centralized address storage
|
|
* for the given address data
|
|
*/
|
|
static nhdp_addr_t *get_nhdp_db_addr(uint8_t *addr, uint8_t prefix)
|
|
{
|
|
switch (prefix) {
|
|
case 8:
|
|
return nhdp_addr_db_get_address(addr, 1, AF_CC110X);
|
|
|
|
case 32:
|
|
return nhdp_addr_db_get_address(addr, 4, AF_INET);
|
|
|
|
default:
|
|
if (prefix < 32) {
|
|
return nhdp_addr_db_get_address(addr, 4, AF_INET);
|
|
}
|
|
else {
|
|
return nhdp_addr_db_get_address(addr, 16, AF_INET6);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process address lists from the HELLO msg in the information bases
|
|
*/
|
|
static void process_temp_tables(void)
|
|
{
|
|
nib_entry_t *nib_elt;
|
|
timex_t now;
|
|
|
|
vtimer_now(&now);
|
|
iib_update_lt_status(&now);
|
|
|
|
nib_elt = nib_process_hello(nb_addr_list_head, &rem_addr_list_head);
|
|
|
|
if (nib_elt) {
|
|
iib_process_hello(if_pid, nib_elt, send_addr_list_head, th_sym_addr_list_head,
|
|
th_rem_addr_list_head, rem_addr_list_head, val_time, sym, lost);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Free all allocated space for the temporary address lists
|
|
*/
|
|
static void cleanup_temp_addr_lists(void)
|
|
{
|
|
nhdp_free_addr_list(send_addr_list_head);
|
|
nhdp_free_addr_list(nb_addr_list_head);
|
|
nhdp_free_addr_list(th_sym_addr_list_head);
|
|
nhdp_free_addr_list(th_rem_addr_list_head);
|
|
nhdp_free_addr_list(rem_addr_list_head);
|
|
send_addr_list_head = NULL;
|
|
nb_addr_list_head = NULL;
|
|
th_sym_addr_list_head = NULL;
|
|
th_rem_addr_list_head = NULL;
|
|
rem_addr_list_head = NULL;
|
|
}
|