/* * 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 Implementation of NHDP's core functionality * * @author Fabian Nack * * @} */ #include "net/gnrc/conn.h" #include "net/conn/udp.h" #include "msg.h" #include "net/gnrc/netapi.h" #include "net/gnrc/netif.h" #include "thread.h" #include "utlist.h" #include "mutex.h" #include "net/ipv6/addr.h" #include "rfc5444/rfc5444_writer.h" #include "lib_table.h" #include "iib_table.h" #include "nib_table.h" #include "nhdp.h" #include "nhdp_address.h" #include "nhdp_writer.h" #include "nhdp_reader.h" #ifndef MODULE_CONN_UDP #error "nhdp needs a conn_udp implementation to work" #endif #define HELLO_TIMER (12345) char nhdp_stack[NHDP_STACK_SIZE]; char nhdp_rcv_stack[NHDP_STACK_SIZE]; /* Internal variables */ static kernel_pid_t nhdp_pid = KERNEL_PID_UNDEF; static kernel_pid_t nhdp_rcv_pid = KERNEL_PID_UNDEF; static kernel_pid_t helper_pid = KERNEL_PID_UNDEF; static nhdp_if_entry_t nhdp_if_table[GNRC_NETIF_NUMOF]; static mutex_t send_rcv_mutex = MUTEX_INIT; static conn_udp_t conn; #if (NHDP_METRIC_NEEDS_TIMER) static xtimer_t metric_timer; static timex_t metric_interval; static msg_t metric_msg; #endif /* Internal function prototypes */ static void *_nhdp_runner(void *arg __attribute__((unused))); static void *_nhdp_receiver(void *arg __attribute__((unused))); static void write_packet(struct rfc5444_writer *wr __attribute__((unused)), struct rfc5444_writer_target *iface __attribute__((unused)), void *buffer, size_t length); /*---------------------------------------------------------------------------* * NHDP Core API * *---------------------------------------------------------------------------*/ void nhdp_init(void) { if (nhdp_pid != KERNEL_PID_UNDEF) { /* do not initialize twice */ return; } /* Prepare interface table */ for (int i = 0; i < GNRC_NETIF_NUMOF; i++) { nhdp_if_table[i].if_pid = KERNEL_PID_UNDEF; memset(&nhdp_if_table[i].wr_target, 0, sizeof(struct rfc5444_writer_target)); } /* Initialize reader and writer */ nhdp_writer_init(); nhdp_reader_init(); } kernel_pid_t nhdp_start(void) { if (nhdp_pid == KERNEL_PID_UNDEF) { /* Init destination address for NHDP's packets */ /* Start the NHDP thread */ nhdp_pid = thread_create(nhdp_stack, sizeof(nhdp_stack), THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST, _nhdp_runner, NULL, "NHDP"); #if (NHDP_METRIC_NEEDS_TIMER) /* Configure periodic timer message to refresh metric values */ if (nhdp_pid != KERNEL_PID_UNDEF) { metric_interval = timex_from_uint64(DAT_REFRESH_INTERVAL * SEC_IN_USEC); metric_msg.type = NHDP_METRIC_TIMER; metric_msg.content.ptr = NULL; xtimer_set_msg64(&metric_timer, timex_uint64(metric_interval), metric_msg, nhdp_pid); } #endif } return nhdp_pid; } int nhdp_register_if_default(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size, uint8_t addr_type, uint16_t max_pl_size) { return nhdp_register_if(if_pid, addr, addr_size, addr_type, max_pl_size, NHDP_DEFAULT_HELLO_INT_MS, NHDP_DEFAULT_HOLD_TIME_MS); } int nhdp_register_if(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size, uint8_t addr_type, uint16_t max_pl_size, uint16_t hello_int_ms, uint16_t val_time_ms) { nhdp_if_entry_t *if_entry = NULL; nhdp_addr_t *nhdp_addr; msg_t signal_msg; if (nhdp_rcv_pid != KERNEL_PID_UNDEF) { return -2; } for (int i = 0; i < GNRC_NETIF_NUMOF; i++) { if (nhdp_if_table[i].if_pid == KERNEL_PID_UNDEF) { if_entry = &nhdp_if_table[i]; break; } } if (!if_entry) { /* Maximum number of registerable interfaces reached */ return -2; } uint16_t payload_size = max_pl_size > NHDP_MAX_RFC5444_PACKET_SZ ? NHDP_MAX_RFC5444_PACKET_SZ : max_pl_size; if_entry->wr_target.packet_buffer = (uint8_t *) calloc(payload_size, sizeof(uint8_t)); if (!if_entry->wr_target.packet_buffer) { /* Insufficient memory */ return -1; } if_entry->wr_target.packet_size = payload_size; if_entry->wr_target.sendPacket = write_packet; /* Get NHDP address entry for the given address */ nhdp_addr = nhdp_addr_db_get_address(addr, addr_size, addr_type); if (!nhdp_addr) { /* Insufficient memory */ free(if_entry->wr_target.packet_buffer); return -1; } /* Add the interface to the LIB */ if (lib_add_if_addr(if_pid, nhdp_addr) != 0) { free(if_entry->wr_target.packet_buffer); nhdp_decrement_addr_usage(nhdp_addr); return -1; } /* Create new IIB for the interface */ if (iib_register_if(if_pid) != 0) { /* TODO: Cleanup lib entry */ free(if_entry->wr_target.packet_buffer); nhdp_decrement_addr_usage(nhdp_addr); return -1; } /* Set Interface's PID */ if_entry->if_pid = if_pid; /* Set HELLO_INTERVAL and H_HOLD_TIME (validity time) */ if_entry->hello_interval.seconds = 0; if_entry->hello_interval.microseconds = MS_IN_USEC * hello_int_ms; if_entry->validity_time.seconds = 0; if_entry->validity_time.microseconds = MS_IN_USEC * val_time_ms; timex_normalize(&if_entry->hello_interval); timex_normalize(&if_entry->validity_time); /* Reset sequence number */ if_entry->seq_no = 0; /* Everything went well */ nhdp_decrement_addr_usage(nhdp_addr); nhdp_writer_register_if(&if_entry->wr_target); helper_pid = if_pid; /* Start the receiving thread */ nhdp_rcv_pid = thread_create(nhdp_rcv_stack, sizeof(nhdp_rcv_stack), THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST, _nhdp_receiver, NULL, "nhdp_rcv_thread"); /* Start sending periodic HELLO */ signal_msg.type = HELLO_TIMER; signal_msg.content.ptr = if_entry; /* TODO: msg_send or msg_try_send? */ msg_try_send(&signal_msg, nhdp_pid); return 0; } int nhdp_register_non_manet_if(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size, uint8_t addr_type) { return nhdp_add_address(if_pid, addr, addr_size, addr_type); } int nhdp_add_address(kernel_pid_t if_pid, uint8_t *addr, size_t addr_size, uint8_t addr_type) { int result; /* Get NHDP address entry for the given address */ nhdp_addr_t *nhdp_addr = nhdp_addr_db_get_address(addr, addr_size, addr_type); if (!nhdp_addr) { /* Insufficient memory */ return -1; } result = lib_add_if_addr(if_pid, nhdp_addr); nhdp_decrement_addr_usage(nhdp_addr); return result; } /*------------------------------------------------------------------------------------*/ /* Internal functions */ /*------------------------------------------------------------------------------------*/ /** * Function executed by NHDP thread receiving messages in an endless loop */ static void *_nhdp_runner(void *arg) { nhdp_if_entry_t *if_entry; msg_t msg_rcvd, msg_queue[NHDP_MSG_QUEUE_SIZE]; (void)arg; msg_init_queue(msg_queue, NHDP_MSG_QUEUE_SIZE); while (1) { msg_receive(&msg_rcvd); switch (msg_rcvd.type) { case HELLO_TIMER: mutex_lock(&send_rcv_mutex); if_entry = msg_rcvd.content.ptr; nhdp_writer_send_hello(if_entry); /* TODO: Add jitter */ /* Schedule next sending */ xtimer_set_msg64(&if_entry->if_timer, timex_uint64(if_entry->hello_interval), &msg_rcvd, thread_getpid()); mutex_unlock(&send_rcv_mutex); break; #if (NHDP_METRIC_NEEDS_TIMER) case NHDP_METRIC_TIMER: mutex_lock(&send_rcv_mutex); /* Process necessary metric computations */ iib_process_metric_refresh(); /* Schedule next sending */ metric_msg.type = NHDP_METRIC_TIMER; metric_msg.content.ptr = NULL; xtimer_set_msg64(&metric_timer, timex_uint64(metric_interval), metric_msg, thread_getpid()); mutex_unlock(&send_rcv_mutex); break; #endif default: break; } } return 0; } /** * Receive HELLOs over the configured socket and handle them */ static void *_nhdp_receiver(void *arg __attribute__((unused))) { char nhdp_rcv_buf[NHDP_MAX_RFC5444_PACKET_SZ]; msg_t msg_q[NHDP_MSG_QUEUE_SIZE]; msg_init_queue(msg_q, NHDP_MSG_QUEUE_SIZE); /* Configure socket address for the manet port 269 */ ipv6_addr_t unspec = IPV6_ADDR_UNSPECIFIED; /* Bind UDP socket to socket address */ if (conn_udp_create(&conn, &unspec, sizeof(unspec), AF_INET6, MANET_PORT) == -1) { /* Failed creating the connection */ return 0; } while (1) { ipv6_addr_t rcv_addr; uint16_t rcv_port; size_t addr_len = sizeof(rcv_addr); int32_t rcv_size = conn_udp_recvfrom(&conn, (void *)nhdp_rcv_buf, NHDP_MAX_RFC5444_PACKET_SZ, &rcv_addr, &addr_len, &rcv_port); if (rcv_size > 0) { /* Packet received, let the reader handle it */ mutex_lock(&send_rcv_mutex); nhdp_reader_handle_packet(helper_pid, (void *)nhdp_rcv_buf, rcv_size); mutex_unlock(&send_rcv_mutex); } } conn_udp_close(&conn); return 0; } /** * Send packet for the registered interface * Called by oonf_api to send packet over the configured socket */ static void write_packet(struct rfc5444_writer *wr __attribute__((unused)), struct rfc5444_writer_target *iface __attribute__((unused)), void *buffer, size_t length) { ipv6_addr_t src, dst = IPV6_ADDR_ALL_NODES_LINK_LOCAL; uint16_t sport, dport = MANET_PORT; conn_udp_getlocaladdr(&conn, &src, &sport); conn_udp_sendto(buffer, length, &src, sizeof(src), &dst, sizeof(dst), AF_INET, sport, dport); }