1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/sys/net/routing/nhdp/nhdp_reader.c
2015-05-27 11:05:13 +02:00

472 lines
16 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 iib_link_set_entry_t *originator_link_tuple = NULL;
static uint32_t lt_metric_val = NHDP_METRIC_UNKNOWN;
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_pkt_end_cb(struct rfc5444_reader_tlvblock_context *context,
bool dropped);
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 add_temp_metric_value(nhdp_addr_t *address);
static void process_temp_tables(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 },
[RFC5444_ADDRTLV_LINK_METRIC] = { .type = RFC5444_ADDRTLV_LINK_METRIC, .type_ext = NHDP_METRIC,
.match_type_ext = true, .min_length = 0x02,
.max_length = 0x02, .match_length = true },
};
/* oonf_api packet consumer used for RFC5444 packet consumption */
static struct rfc5444_reader_tlvblock_consumer _nhdp_packet_consumer = {
.end_callback = _nhdp_pkt_end_cb,
};
/* 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)
{
/* Initialize reader */
rfc5444_reader_init(&reader);
/* Register packet consumer for sequence number processing */
rfc5444_reader_add_packet_consumer(&reader, &_nhdp_packet_consumer, NULL, 0);
/* 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)
{
rfc5444_reader_cleanup(&reader);
}
/*------------------------------------------------------------------------------------*/
/* Internal functions */
/*------------------------------------------------------------------------------------*/
/**
* Process metric steps for packet with packet sequence number
* Called by oonf_api after the whole packet was processed
*/
static enum rfc5444_result _nhdp_pkt_end_cb(struct rfc5444_reader_tlvblock_context *context,
bool dropped __attribute__((unused)))
{
/* Process metric changes */
if ((originator_link_tuple != NULL) && (context->has_pktseqno)) {
iib_process_metric_pckt(originator_link_tuple, lt_metric_val, context->pkt_seqno);
}
/* Reset originator temp fields */
originator_link_tuple = NULL;
lt_metric_val = NHDP_METRIC_UNKNOWN;
return RFC5444_OKAY;
}
/**
* 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;
/* Get NHDP address for the current netaddr */
nhdp_addr_t *current_addr = get_nhdp_db_addr(&cont->addr._addr[0], cont->addr._prefix_len);
if (!current_addr) {
/* Insufficient memory */
return RFC5444_DROP_MESSAGE;
}
/* Check validity of address tlvs */
if (check_addr_validity(current_addr) != RFC5444_OKAY) {
nhdp_decrement_addr_usage(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:
current_addr->in_tmp_table = NHDP_ADDR_TMP_SEND_LIST;
break;
case RFC5444_LOCALIF_OTHER_IF:
current_addr->in_tmp_table = NHDP_ADDR_TMP_NB_LIST;
break;
default:
/* Wrong value, drop message */
nhdp_decrement_addr_usage(current_addr);
return RFC5444_DROP_MESSAGE;
}
}
else if ((tmp_result = lib_is_reg_addr(if_pid, current_addr))) {
/* 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_decrement_addr_usage(current_addr);
return RFC5444_DROP_MESSAGE;
}
}
if (lt_metric_val == NHDP_METRIC_UNKNOWN
&& _nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_METRIC].tlv != NULL) {
/* Determine our outgoing link metric value to the originator interface */
uint16_t metric_enc = *((uint16_t*)_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_METRIC]
.tlv->single_value);
if (metric_enc & NHDP_KD_LM_INC) {
/* Incoming metric value at the neighbor if is outgoing value for our if */
lt_metric_val = rfc5444_metric_decode(metric_enc);
}
}
/* Address is one of our own addresses, ignore it */
nhdp_decrement_addr_usage(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:
add_temp_metric_value(current_addr);
current_addr->in_tmp_table = NHDP_ADDR_TMP_TH_SYM_LIST;
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 */
add_temp_metric_value(current_addr);
current_addr->in_tmp_table = NHDP_ADDR_TMP_TH_SYM_LIST;
}
else {
current_addr->in_tmp_table = NHDP_ADDR_TMP_TH_REM_LIST;
}
break;
default:
/* Wrong value, drop message */
nhdp_decrement_addr_usage(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:
add_temp_metric_value(current_addr);
current_addr->in_tmp_table = NHDP_ADDR_TMP_TH_SYM_LIST;
break;
case RFC5444_OTHERNEIGHB_LOST:
current_addr->in_tmp_table = NHDP_ADDR_TMP_TH_REM_LIST;
break;
default:
/* Wrong value, drop message */
nhdp_decrement_addr_usage(current_addr);
return RFC5444_DROP_MESSAGE;
}
}
else {
/* Addresses without expected TLV are ignored */
nhdp_decrement_addr_usage(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;
nhdp_reset_addresses_tmp_usg(1);
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);
}
}
}
/**
* Add a metric value to the address if a corresponding TLV exists in the message
*/
static void add_temp_metric_value(nhdp_addr_t *address)
{
if (_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_METRIC].tlv) {
uint16_t metric_enc = *((uint16_t*)_nhdp_addr_tlvs[RFC5444_ADDRTLV_LINK_METRIC]
.tlv->single_value);
if (metric_enc & (NHDP_KD_LM_INC | NHDP_KD_NM_INC)) {
address->tmp_metric_val = metric_enc;
}
}
}
/**
* 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();
if (nib_elt) {
originator_link_tuple = iib_process_hello(if_pid, nib_elt, val_time, sym, lost);
if (originator_link_tuple) {
iib_process_metric_msg(originator_link_tuple, int_time != 0 ? int_time : val_time);
}
}
}