1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/sys/net/network_layer/sixlowpan/lowpan.c
Oleg Hahm 26710c1085 Merge pull request #2499 from gebart/pr/lowpan-address-decoding-fixes
sixlowpan: Handle 16-bit addresses correctly (both decode and encode)
2015-04-01 11:36:56 +02:00

1790 lines
56 KiB
C

/*
* 6lowpan implementation
*
* Copyright (C) 2013 INRIA.
*
* 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 sixlowpan
* @{
* @file sixlowpan.c
* @brief 6lowpan functions
* @author Stephan Zeisberg <zeisberg@mi.fu-berlin.de>
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
* @author Oliver Gesch <oliver.gesch@googlemail.com>
* @author Eric Engel <eric.engel@fu-berlin.de>
* @}
*/
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include "vtimer.h"
#include "timex.h"
#include "thread.h"
#include "mutex.h"
#include "hwtimer.h"
#include "msg.h"
#include "transceiver.h"
#include "sixlowpan/mac.h"
#include "sixlowpan/ndp.h"
#include "lowpan.h"
#ifdef MODULE_SIXLOWBORDER
#include "border/border.h"
#endif
#include "ip.h"
#include "icmp.h"
#include "ieee802154_frame.h"
#include "socket_base/in.h"
#include "net_help.h"
#define ENABLE_DEBUG (0)
#if ENABLE_DEBUG
static char addr_str[IPV6_MAX_ADDR_STR_LEN];
#endif
#include "debug.h"
#define CON_STACKSIZE (KERNEL_CONF_STACKSIZE_DEFAULT)
#define LOWPAN_TRANSFER_BUF_STACKSIZE (KERNEL_CONF_STACKSIZE_DEFAULT)
#define SIXLOWPAN_MAX_REGISTERED (4)
#define LOWPAN_REAS_BUF_TIMEOUT (15 * 1000 * 1000)
/* TODO: Set back to 3 * 1000 * (1000) */
#define IPV6_LL_ADDR_LEN (8)
#define SIXLOWPAN_FRAG_HDR_MASK (0xf8)
typedef struct lowpan_interval_list_t {
uint8_t start;
uint8_t end;
struct lowpan_interval_list_t *next;
} lowpan_interval_list_t;
/**
* @brief 6LoWPAN reassembly buffer.
*
* @see <a href="http://tools.ietf.org/html/rfc4944#section-5.3">
* RFC 4944, section 5.3
* </a>
*/
typedef struct lowpan_reas_buf_t {
net_if_eui64_t s_addr; ///< Source address
net_if_eui64_t d_addr; ///< Destination address
uint16_t tag; ///< Fragment tag
timex_t timestamp; ///< Timestamp of last packet fragment
/**
* @brief Size of reassembled packet with possible IPHC header
*/
uint16_t packet_size;
/**
* @brief Additive size of currently already received fragments
*/
uint16_t current_packet_size;
/**
* @brief Pointer to allocated memory for reassembled packet + 6LoWPAN
* Dispatch Byte
*/
uint8_t *packet;
/**
* @brief Pointer to list of intervals of received packet fragments
* (if any)
*/
lowpan_interval_list_t *interval_list_head;
struct lowpan_reas_buf_t *next;
} lowpan_reas_buf_t;
extern mutex_t lowpan_context_mutex;
uint16_t tag = 0;
uint8_t max_frag_initial = 0;
uint8_t max_frag;
static uint16_t packet_length;
static sixlowpan_lowpan_iphc_status_t iphc_status = LOWPAN_IPHC_ENABLE;
static ipv6_hdr_t *ipv6_buf;
static lowpan_reas_buf_t *head = NULL;
static lowpan_reas_buf_t *packet_fifo = NULL;
/* length of compressed packet */
uint16_t comp_len;
uint8_t frag_size;
uint8_t reas_buf[512];
uint8_t comp_buf[512];
uint8_t first_frag = 0;
mutex_t fifo_mutex = MUTEX_INIT;
kernel_pid_t ip_process_pid = KERNEL_PID_UNDEF;
kernel_pid_t nd_nbr_cache_rem_pid = KERNEL_PID_UNDEF;
kernel_pid_t contexts_rem_pid = KERNEL_PID_UNDEF;
kernel_pid_t transfer_pid = KERNEL_PID_UNDEF;
mutex_t lowpan_context_mutex = MUTEX_INIT;
/* registered upper layer threads */
kernel_pid_t sixlowpan_reg[SIXLOWPAN_MAX_REGISTERED];
static sixlowpan_lowpan_frame_t current_frame;
char ip_process_buf[IP_PROCESS_STACKSIZE];
char con_buf[CON_STACKSIZE];
char lowpan_transfer_buf[LOWPAN_TRANSFER_BUF_STACKSIZE];
lowpan_context_t contexts[NDP_6LOWPAN_CONTEXT_MAX];
uint8_t context_len = 0;
uint16_t local_address = 0;
int lowpan_init(int as_border);
uint8_t lowpan_iphc_encoding(int if_id, const uint8_t *dest, int dest_len,
ipv6_hdr_t *ipv6_buf_extra, uint8_t *ptr);
void lowpan_iphc_decoding(uint8_t *data, uint8_t length, net_if_eui64_t *s_addr,
net_if_eui64_t *d_addr);
void add_fifo_packet(lowpan_reas_buf_t *current_packet);
lowpan_reas_buf_t *collect_garbage_fifo(lowpan_reas_buf_t *current_buf);
lowpan_reas_buf_t *collect_garbage(lowpan_reas_buf_t *current_buf);
void init_reas_bufs(lowpan_reas_buf_t *buf);
void check_timeout(void);
void print_long_local_addr(net_if_eui64_t *saddr);
lowpan_context_t *lowpan_context_lookup(ipv6_addr_t *addr);
/* deliver packet to mac*/
int sixlowpan_lowpan_sendto(int if_id, const void *dest, int dest_len,
uint8_t *data, uint16_t data_len)
{
uint8_t mcast = 0;
ipv6_buf = (ipv6_hdr_t *) data;
uint16_t send_packet_length = data_len;
if (ipv6_addr_is_multicast(&ipv6_buf->destaddr)) {
/* send broadcast */
mcast = 1;
}
/* check if packet needs to be fragmented */
DEBUG("sixlowpan_lowpan_sendto(%d, dest, %d, data, %"PRIu16")\n",
if_id, dest_len, data_len);
#if ENABLE_DEBUG
DEBUG("dest: ");
if (dest_len == 8) {
print_long_local_addr((net_if_eui64_t *)dest);
}
else {
printf("0x%04"PRIx16"\n", NTOHS(*((uint16_t *)dest)));
}
DEBUG("data: \n");
for (int i = 0; i < data_len; i++) {
printf("%02x ", data[i]);
if (!((i + 1) % 16) || i == data_len - 1) {
printf("\n");
}
}
#endif
if (iphc_status == LOWPAN_IPHC_ENABLE) {
if (!lowpan_iphc_encoding(if_id, dest, dest_len, ipv6_buf, data)) {
return -1;
}
data = &comp_buf[0];
send_packet_length = comp_len;
}
else {
memmove(data + 1, data, data_len);
data[0] = SIXLOWPAN_IPV6_DISPATCH;
send_packet_length++;
}
if (send_packet_length > PAYLOAD_SIZE - IEEE_802154_MAX_HDR_LEN) {
uint16_t remaining;
uint16_t position, datagram_size = send_packet_length;
uint8_t max_frame;
if (net_if_get_interface(if_id)->transceivers & (IEEE802154_TRANSCEIVER)) {
max_frame = PAYLOAD_SIZE;
}
else {
max_frame = PAYLOAD_SIZE - IEEE_802154_MAX_HDR_LEN;
}
uint8_t fragbuf[max_frame + 5];
/* first fragment */
max_frag_initial = ((max_frame - 4) / 8) * 8;
if (data[0] == (char)SIXLOWPAN_IPV6_DISPATCH) {
/* XXX: weird, but only this way we get correct packet output */
max_frag_initial++;
datagram_size--;
}
memcpy(&fragbuf[4], data, max_frag_initial);
fragbuf[0] = ((SIXLOWPAN_FRAG1_DISPATCH << 8) | datagram_size) >> 8;
fragbuf[1] = (SIXLOWPAN_FRAG1_DISPATCH << 8) | datagram_size;
fragbuf[2] = tag >> 8;
fragbuf[3] = tag;
sixlowpan_mac_send_ieee802154_frame(if_id, dest, dest_len,
&fragbuf,
max_frag_initial + 4,
mcast);
/* subsequent fragments */
position = max_frag_initial;
max_frag = ((max_frame - 5) / 8) * 8;
data += position;
while (send_packet_length - position > max_frame - 5) {
memset(&fragbuf[0], 0, sizeof(fragbuf));
memcpy(&fragbuf[5], data, max_frag);
fragbuf[0] = ((SIXLOWPAN_FRAGN_DISPATCH << 8) | datagram_size) >> 8;
fragbuf[1] = (SIXLOWPAN_FRAGN_DISPATCH << 8) | datagram_size;
fragbuf[2] = tag >> 8;
fragbuf[3] = tag;
fragbuf[4] = position / 8;
sixlowpan_mac_send_ieee802154_frame(if_id, dest, dest_len,
&fragbuf,
max_frag + 5, mcast);
data += max_frag;
position += max_frag;
}
remaining = send_packet_length - position;
memset(&fragbuf[0], 0, sizeof(fragbuf));
memcpy(&fragbuf[5], data, remaining);
fragbuf[0] = ((SIXLOWPAN_FRAGN_DISPATCH << 8) | datagram_size) >> 8;
fragbuf[1] = (SIXLOWPAN_FRAGN_DISPATCH << 8) | datagram_size;
fragbuf[2] = tag >> 8;
fragbuf[3] = tag;
fragbuf[4] = position / 8;
tag++;
if (sixlowpan_mac_send_ieee802154_frame(if_id, dest, dest_len,
&fragbuf, remaining + 5, mcast) < 0) {
return -1;
}
}
else {
return sixlowpan_mac_send_ieee802154_frame(if_id, dest, dest_len, data,
send_packet_length, mcast);
}
return data_len;
}
void sixlowpan_lowpan_set_iphc_status(
sixlowpan_lowpan_iphc_status_t status)
{
iphc_status = status;
}
#if ENABLE_DEBUG
void print_long_local_addr(net_if_eui64_t *saddr)
{
printf("%02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
((uint8_t *)saddr)[0], ((uint8_t *)saddr)[1], ((uint8_t *)saddr)[2],
((uint8_t *)saddr)[3], ((uint8_t *)saddr)[4], ((uint8_t *)saddr)[5],
((uint8_t *)saddr)[6], ((uint8_t *)saddr)[7]);
}
void sixlowpan_lowpan_print_reassembly_buffers(void)
{
lowpan_reas_buf_t *temp_buffer;
lowpan_interval_list_t *temp_interval;
temp_buffer = head;
printf("\n\n--- Reassembly Buffers ---\n");
while (temp_buffer != NULL) {
print_long_local_addr(&temp_buffer->s_addr);
printf("Ident.: %i, Packet Size: %i/%i, Timestamp: %"PRIu64"\n",
temp_buffer->tag, temp_buffer->current_packet_size,
temp_buffer->packet_size, timex_uint64(temp_buffer->timestamp));
temp_interval = temp_buffer->interval_list_head;
while (temp_interval != NULL) {
printf("\t%i - %i\n", temp_interval->start, temp_interval->end);
temp_interval = temp_interval->next;
}
temp_buffer = temp_buffer->next;
}
}
void sixlowpan_lowpan_print_fifo_buffers(void)
{
lowpan_reas_buf_t *temp_buffer;
lowpan_interval_list_t *temp_interval;
temp_buffer = packet_fifo;
printf("\n\n--- Reassembly Buffers ---\n");
while (temp_buffer != NULL) {
print_long_local_addr(&temp_buffer->s_addr);
printf("Ident.: %i, Packet Size: %i/%i, Timestamp: %"PRIu64"\n",
temp_buffer->tag, temp_buffer->current_packet_size,
temp_buffer->packet_size, timex_uint64(temp_buffer->timestamp));
temp_interval = temp_buffer->interval_list_head;
while (temp_interval != NULL) {
printf("\t%i - %i\n", temp_interval->start, temp_interval->end);
temp_interval = temp_interval->next;
}
temp_buffer = temp_buffer->next;
}
}
#endif
static void *lowpan_transfer(void *arg)
{
(void) arg;
msg_t m_recv, m_send;
ipv6_hdr_t *ipv6_buf;
lowpan_reas_buf_t *current_buf;
while (1) {
uint8_t gotosleep = 1;
mutex_lock(&fifo_mutex);
current_buf = packet_fifo;
if (current_buf != NULL) {
mutex_unlock(&fifo_mutex);
if (current_buf->packet[0] == SIXLOWPAN_IPV6_DISPATCH) {
DEBUG("INFO: Uncompressed IPv6 dispatch (0x%02x) received\n",
current_buf->packet[0]);
ipv6_buf = ipv6_get_buf();
memcpy(ipv6_buf, (current_buf->packet) + 1, current_buf->packet_size - 1);
m_send.content.ptr = (char *)ipv6_buf;
packet_length = current_buf->packet_size - 1;
msg_send_receive(&m_send, &m_recv, ip_process_pid);
}
else if (((current_buf->packet[0] & 0xf0) == IPV6_VER) &&
(iphc_status == LOWPAN_IPHC_DISABLE)) {
ipv6_buf = ipv6_get_buf();
memcpy(ipv6_buf, (current_buf->packet), current_buf->packet_size);
m_send.content.ptr = (char *)ipv6_buf;
packet_length = current_buf->packet_size;
msg_send_receive(&m_send, &m_recv, ip_process_pid);
}
else if (((current_buf->packet[0] & 0xe0) == SIXLOWPAN_IPHC1_DISPATCH) &&
(iphc_status == LOWPAN_IPHC_ENABLE)) {
DEBUG("INFO: IPHC1 dispatch 0x%02x received, decompress\n",
current_buf->packet[0]);
lowpan_iphc_decoding(current_buf->packet,
current_buf->packet_size,
&(current_buf->s_addr),
&(current_buf->d_addr));
ipv6_buf = ipv6_get_buf();
m_send.content.ptr = (char *) ipv6_buf;
msg_send_receive(&m_send, &m_recv, ip_process_pid);
}
else {
DEBUG("ERROR: packet with unknown dispatch 0x%02x received\n",
current_buf->packet[0]);
}
collect_garbage_fifo(current_buf);
gotosleep = 0;
}
if (gotosleep == 1) {
mutex_unlock(&fifo_mutex);
thread_sleep();
}
}
return NULL;
}
uint8_t ll_get_addr_match(net_if_eui64_t *src, net_if_eui64_t *dst)
{
uint8_t val = 0, xor;
for (uint8_t i = 0; i < sizeof(net_if_eui64_t); i++) {
/* if bytes are equal add 8 */
if (src->uint8[i] == dst->uint8[i]) {
val += 8;
}
else {
xor = src->uint8[i] ^ dst->uint8[i];
/* while bits from byte equal add 1 */
for (int j = 0; j < 8; j++) {
if ((xor & 0x80) == 0) {
val++;
xor = xor << 1;
}
else {
break;
}
}
}
}
return val;
}
lowpan_reas_buf_t *new_packet_buffer(uint16_t datagram_size,
uint16_t datagram_tag,
net_if_eui64_t *s_addr,
net_if_eui64_t *d_addr,
lowpan_reas_buf_t *current_buf,
lowpan_reas_buf_t *temp_buf)
{
lowpan_reas_buf_t *new_buf = NULL;
/* Allocate new memory for a new packet to be reassembled */
new_buf = malloc(sizeof(lowpan_reas_buf_t));
if (new_buf != NULL) {
init_reas_bufs(new_buf);
new_buf->packet = malloc(datagram_size);
if (new_buf->packet != NULL) {
memcpy(&new_buf->s_addr, s_addr, 8);
memcpy(&new_buf->d_addr, d_addr, 8);
new_buf->tag = datagram_tag;
new_buf->packet_size = datagram_size;
vtimer_now(&new_buf->timestamp);
if ((current_buf == NULL) && (temp_buf == NULL)) {
head = new_buf;
}
else {
temp_buf->next = new_buf;
}
return new_buf;
}
else {
free(new_buf);
return NULL;
}
}
else {
return NULL;
}
}
lowpan_reas_buf_t *get_packet_frag_buf(uint16_t datagram_size,
uint16_t datagram_tag,
net_if_eui64_t *s_addr,
net_if_eui64_t *d_addr)
{
lowpan_reas_buf_t *current_buf = NULL, *temp_buf = NULL;
current_buf = head;
while (current_buf != NULL) {
if (((ll_get_addr_match(&current_buf->s_addr, s_addr)) == 64) &&
((ll_get_addr_match(&current_buf->d_addr, d_addr)) == 64) &&
(current_buf->packet_size == datagram_size) &&
(current_buf->tag == datagram_tag) &&
current_buf->interval_list_head != NULL) {
/* Found buffer for current packet fragment */
vtimer_now(&current_buf->timestamp);
return current_buf;
}
temp_buf = current_buf;
current_buf = current_buf->next;
}
return new_packet_buffer(datagram_size, datagram_tag, s_addr, d_addr,
current_buf, temp_buf);
}
uint8_t is_in_interval(uint8_t start1, uint8_t end1, uint8_t start2, uint8_t end2)
{
/* 1: Interval 1 and 2 are the same or overlapping */
/* 0: Interval 1 and 2 are not overlapping or the same */
if (((start1 < start2) && (start2 <= end1)) ||
((start2 < start1) && (start1 <= end2)) ||
((start1 == start2) && (end1 == end2))) {
return 1;
}
else {
return 0;
}
}
uint8_t handle_packet_frag_interval(lowpan_reas_buf_t *current_buf,
uint8_t datagram_offset, uint8_t frag_size)
{
/* 0: Error, discard fragment */
/* 1: Finished correctly */
lowpan_interval_list_t *temp_interval = NULL, *current_interval = NULL, *new_interval = NULL;
current_interval = current_buf->interval_list_head;
while (current_interval != NULL) {
if (is_in_interval(current_interval->start, current_interval->end, datagram_offset, datagram_offset + frag_size) == 1) {
/* Interval is overlapping or the same as one of a previous fragment, discard fragment */
return 0;
}
temp_interval = current_interval;
current_interval = current_interval->next;
}
new_interval = malloc(sizeof(lowpan_interval_list_t));
if (new_interval != NULL) {
new_interval->start = datagram_offset;
new_interval->end = datagram_offset + frag_size - 1;
new_interval->next = NULL;
if ((current_interval == NULL) && (temp_interval == NULL)) {
current_buf->interval_list_head = new_interval;
}
else {
temp_interval->next = new_interval;
}
return 1;
}
return 0;
}
lowpan_reas_buf_t *collect_garbage_fifo(lowpan_reas_buf_t *current_buf)
{
lowpan_interval_list_t *temp_list, *current_list;
lowpan_reas_buf_t *temp_buf, *my_buf, *return_buf;
mutex_lock(&fifo_mutex);
temp_buf = packet_fifo;
my_buf = temp_buf;
if (packet_fifo == current_buf) {
packet_fifo = current_buf->next;
return_buf = packet_fifo;
}
else {
while (temp_buf != current_buf) {
my_buf = temp_buf;
temp_buf = temp_buf->next;
}
my_buf->next = current_buf->next;
return_buf = my_buf->next;
}
mutex_unlock(&fifo_mutex);
current_list = current_buf->interval_list_head;
temp_list = current_list;
while (current_list != NULL) {
temp_list = current_list->next;
free(current_list);
current_list = temp_list;
}
free(current_buf->packet);
free(current_buf);
return return_buf;
}
lowpan_reas_buf_t *collect_garbage(lowpan_reas_buf_t *current_buf)
{
lowpan_interval_list_t *temp_list, *current_list;
lowpan_reas_buf_t *temp_buf, *my_buf, *return_buf;
temp_buf = head;
my_buf = temp_buf;
if (head == current_buf) {
head = current_buf->next;
return_buf = head;
}
else {
while (temp_buf != current_buf) {
my_buf = temp_buf;
temp_buf = temp_buf->next;
}
my_buf->next = current_buf->next;
return_buf = my_buf->next;
}
current_list = current_buf->interval_list_head;
temp_list = current_list;
while (current_list != NULL) {
temp_list = current_list->next;
free(current_list);
current_list = temp_list;
}
free(current_buf->packet);
free(current_buf);
return return_buf;
}
void handle_packet_fragment(uint8_t *data, uint8_t datagram_offset,
uint16_t datagram_size, uint16_t datagram_tag,
net_if_eui64_t *s_addr, net_if_eui64_t *d_addr,
uint8_t hdr_length, uint8_t frag_size)
{
lowpan_reas_buf_t *current_buf;
/* Is there already a reassembly buffer for this packet fragment? */
current_buf = get_packet_frag_buf(datagram_size, datagram_tag, s_addr,
d_addr);
if ((current_buf != NULL) && (handle_packet_frag_interval(current_buf,
datagram_offset,
frag_size) == 1)) {
/* Copy fragment bytes into corresponding packet space area */
memcpy(current_buf->packet + datagram_offset, data + hdr_length, frag_size);
current_buf->current_packet_size += frag_size;
if (current_buf->current_packet_size == current_buf->packet_size) {
add_fifo_packet(current_buf);
if (thread_getstatus(transfer_pid) == STATUS_SLEEPING) {
thread_wakeup(transfer_pid);
}
}
}
else {
/* No memory left or duplicate */
if (current_buf == NULL) {
printf("ERROR: no memory left!\n");
}
else {
printf("ERROR: duplicate fragment!\n");
}
}
}
void check_timeout(void)
{
lowpan_reas_buf_t *temp_buf, *smallest_time = NULL;
int count = 0;
timex_t now;
vtimer_now(&now);
temp_buf = head;
while (temp_buf != NULL) {
if ((timex_uint64(now) - timex_uint64(temp_buf->timestamp)) >= LOWPAN_REAS_BUF_TIMEOUT) {
printf("TIMEOUT!cur_time: %" PRIu64 ", temp_buf: %" PRIu64 "\n", timex_uint64(now),
timex_uint64(temp_buf->timestamp));
temp_buf = collect_garbage(temp_buf);
}
else {
if (smallest_time == NULL) {
smallest_time = temp_buf;
}
else if (timex_cmp(temp_buf->timestamp, smallest_time->timestamp) < 0) {
smallest_time = temp_buf;
}
temp_buf = temp_buf->next;
count++;
}
}
if ((count > 10) && (smallest_time != NULL)) {
collect_garbage(smallest_time);
}
}
void add_fifo_packet(lowpan_reas_buf_t *current_packet)
{
lowpan_reas_buf_t *temp_buf, *my_buf;
if (head == current_packet) {
head = current_packet->next;
}
else {
temp_buf = head;
while (temp_buf != current_packet) {
my_buf = temp_buf;
temp_buf = temp_buf->next;
}
my_buf->next = current_packet->next;
}
mutex_lock(&fifo_mutex);
if (packet_fifo == NULL) {
packet_fifo = current_packet;
}
else {
temp_buf = packet_fifo;
while (temp_buf != NULL) {
my_buf = temp_buf;
temp_buf = temp_buf->next;
}
my_buf->next = current_packet;
}
mutex_unlock(&fifo_mutex);
current_packet->next = NULL;
}
/* Register an upper layer thread */
uint8_t sixlowpan_lowpan_register(kernel_pid_t pid)
{
uint8_t i;
for (i = 0; (((i < SIXLOWPAN_MAX_REGISTERED) && sixlowpan_reg[i] != pid) &&
(sixlowpan_reg[i] != 0)); i++) {
;
}
if (i >= SIXLOWPAN_MAX_REGISTERED) {
return ENOMEM;
}
else {
sixlowpan_reg[i] = pid;
return 1;
}
}
void lowpan_read(uint8_t *data, uint8_t length, net_if_eui64_t *s_addr,
net_if_eui64_t *d_addr)
{
/* check if packet is fragmented */
short i;
check_timeout();
for (i = 0; i < SIXLOWPAN_MAX_REGISTERED; i++) {
if (sixlowpan_reg[i]) {
msg_t m_send;
m_send.type = LOWPAN_FRAME_RECEIVED;;
current_frame.length = length;
current_frame.data = data;
m_send.content.ptr = (char *) &current_frame;
msg_send(&m_send, sixlowpan_reg[i]);
}
}
/* Fragmented Packet */
if (((data[0] & SIXLOWPAN_FRAG_HDR_MASK) == SIXLOWPAN_FRAG1_DISPATCH) ||
((data[0] & SIXLOWPAN_FRAG_HDR_MASK) == SIXLOWPAN_FRAGN_DISPATCH)) {
uint8_t hdr_length = 0;
uint8_t datagram_offset = 0;
uint16_t datagram_size = 0;
uint16_t datagram_tag = 0;
uint16_t byte_offset;
DEBUG("INFO: fragmentation dispatch 0x%02x received\n",
data[0] & SIXLOWPAN_FRAG_HDR_MASK);
/* get 11-bit from first 2 byte*/
datagram_size = (((uint16_t)(data[0] << 8)) | data[1]) & 0x07ff;
/* get 16-bit datagram tag */
datagram_tag = (((uint16_t)(data[2] << 8)) | data[3]);
switch (data[0] & SIXLOWPAN_FRAG_HDR_MASK) {
/* First Fragment */
case (SIXLOWPAN_FRAG1_DISPATCH): {
datagram_offset = 0;
hdr_length += 4;
if (data[4] == (char)SIXLOWPAN_IPV6_DISPATCH) {
hdr_length++;
}
break;
}
/* Subsequent Fragment */
case (SIXLOWPAN_FRAGN_DISPATCH): {
datagram_offset = data[4];
hdr_length += 5;
break;
}
}
frag_size = length - hdr_length;
byte_offset = datagram_offset * 8;
DEBUG("Frag size is %u, offset is %u, datagram_size is %u\n",
frag_size, byte_offset, datagram_size);
if ((frag_size % 8) != 0) {
if ((byte_offset + frag_size) != datagram_size) {
printf("ERROR: received invalid fragment\n");
return;
}
}
handle_packet_fragment(data, byte_offset, datagram_size, datagram_tag,
s_addr, d_addr, hdr_length, frag_size);
}
/* Regular Packet */
else {
DEBUG("INFO: unfragmentated packet with first byte 0x%02x received\n",
data[0]);
lowpan_reas_buf_t *current_buf = get_packet_frag_buf(length, 0, s_addr, d_addr);
if (current_buf && current_buf->packet) {
/* Copy packet bytes into corresponding packet space area */
memcpy(current_buf->packet, data, length);
current_buf->current_packet_size += length;
add_fifo_packet(current_buf);
}
else {
DEBUG("ERROR: no memory left in packet buffer!\n");
}
if (thread_getstatus(transfer_pid) == STATUS_SLEEPING) {
thread_wakeup(transfer_pid);
}
}
}
/* RFC6282 https://tools.ietf.org/html/rfc6282#section-3.1 */
uint8_t lowpan_iphc_encoding(int if_id, const uint8_t *dest, int dest_len,
ipv6_hdr_t *ipv6_buf_extra, uint8_t *ptr)
{
uint16_t payload_length = NTOHS(ipv6_buf->length);
uint8_t lowpan_iphc[2];
uint8_t *ipv6_hdr_fields = &comp_buf[2];
lowpan_context_t *con = NULL;
uint16_t hdr_pos = 0;
uint8_t tc;
net_if_eui64_t own_iid;
if (net_if_get_src_address_mode(if_id) == NET_IF_TRANS_ADDR_M_SHORT) {
if (!net_if_get_eui64(&own_iid, if_id, 1)) {
return 1;
}
}
else {
if (!net_if_get_eui64(&own_iid, if_id, 0)) {
return 1;
}
}
own_iid.uint8[0] ^= 0x02;
ipv6_buf = ipv6_buf_extra;
memset(&lowpan_iphc, 0, 2);
/* set iphc dispatch */
lowpan_iphc[0] = SIXLOWPAN_IPHC1_DISPATCH;
/* TF: Traffic Class, Flow Label:
* first we need to change DSCP and ECN because in 6lowpan-nd-13 these
* fields are reverse, the original order is DSCP/ECN (rfc 3168) */
tc = (ipv6_buf->version_trafficclass << 4) |
(ipv6_buf->trafficclass_flowlabel >> 4);
tc = (tc >> 2) | (tc << 6);
if ((ipv6_buf->flowlabel == 0) &&
(ipv6_buf->trafficclass_flowlabel & 0x0f) == 0) {
/* flowlabel is elided */
lowpan_iphc[0] |= SIXLOWPAN_IPHC1_FL_C;
if (((ipv6_buf->version_trafficclass & 0x0f) == 0) &&
((ipv6_buf->trafficclass_flowlabel & 0xf0) == 0)) {
/* traffic class is elided */
lowpan_iphc[0] |= SIXLOWPAN_IPHC1_TC_C;
}
else {
/* ECN + DSCP (1 byte), Flow Label is elided */
ipv6_hdr_fields[hdr_pos] = tc;
hdr_pos++;
}
}
else {
/* flowlabel not compressible */
if (((ipv6_buf->version_trafficclass & 0x0f) == 0) &&
((ipv6_buf->trafficclass_flowlabel & 0xf0) == 0)) {
/* traffic class is elided */
lowpan_iphc[0] |= SIXLOWPAN_IPHC1_TC_C;
/* ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided */
ipv6_hdr_fields[hdr_pos] = ((tc & 0xc0) |
(ipv6_buf->trafficclass_flowlabel & 0x0f));
memcpy(&(ipv6_hdr_fields[hdr_pos]), &ipv6_buf->flowlabel , 2);
hdr_pos += 3;
}
else {
/* ECN + DSCP + 4-bit Pad + Flow Label (4 bytes) */
memcpy(&ipv6_hdr_fields[hdr_pos], &ipv6_buf->version_trafficclass, 4);
ipv6_hdr_fields[hdr_pos] = tc;
hdr_pos += 4;
}
}
/* NH: Next Header:
* TODO: NHC */
ipv6_hdr_fields[hdr_pos] = ipv6_buf->nextheader;
hdr_pos++;
/* HLIM: Hop Limit: */
switch (ipv6_buf->hoplimit) {
case (1): {
/* 01: The Hop Limit field is compressed and the hop limit is 1. */
lowpan_iphc[0] |= 0x01;
break;
}
case (64): {
/* 10: The Hop Limit field is compressed and the hop limit is 64. */
lowpan_iphc[0] |= 0x02;
break;
}
case (255): {
/* 11: The Hop Limit field is compressed and the hop limit is 255. */
lowpan_iphc[0] |= 0x03;
break;
}
default: {
ipv6_hdr_fields[hdr_pos] = ipv6_buf->hoplimit;
hdr_pos++;
break;
}
}
mutex_lock(&lowpan_context_mutex);
/* CID: Context Identifier Extension: */
if ((lowpan_context_lookup(&ipv6_buf->srcaddr) != NULL) ||
(lowpan_context_lookup(&ipv6_buf->destaddr) != NULL)) {
lowpan_iphc[1] |= SIXLOWPAN_IPHC2_CID;
memmove(&ipv6_hdr_fields[1], &ipv6_hdr_fields[0], hdr_pos);
hdr_pos++;
}
/* SAC: Source Address Compression */
if (ipv6_addr_is_unspecified(&(ipv6_buf->srcaddr))) {
/* SAC = 1 and SAM = 00 */
lowpan_iphc[1] |= SIXLOWPAN_IPHC2_SAC;
}
else {
if ((con = lowpan_context_lookup(&ipv6_buf->srcaddr)) != NULL) {
/* 1: Source address compression uses stateful, context-based
* compression. */
lowpan_iphc[1] |= SIXLOWPAN_IPHC2_SAC;
ipv6_hdr_fields[0] |= (con->num << 4);
}
if (con || ipv6_addr_is_link_local(&ipv6_buf->srcaddr)) {
/* 0: Source address compression uses stateless compression.*/
if (memcmp(&(ipv6_buf->srcaddr.uint8[8]), &own_iid, 8) == 0) {
/* 0 bits. The address is derived using context information
* and possibly the link-layer addresses.*/
lowpan_iphc[1] |= 0x30;
}
else if ((ipv6_buf->srcaddr.uint32[2] == HTONL(0x000000ffu)) &&
(ipv6_buf->srcaddr.uint16[6] == HTONL(0xfe00u))) {
/* The 48 leading bits of the interface identifier are
* 0000:00FF:FE00, so we can compress the source address-iid to
* 16-bit */
memcpy(&ipv6_hdr_fields[hdr_pos], &ipv6_buf->srcaddr.uint16[7], 2);
hdr_pos += 2;
/* 16 bits. The address is derived using context information
* and the 16 bits carried inline. */
lowpan_iphc[1] |= 0x20;
}
else {
memcpy(&ipv6_hdr_fields[hdr_pos], &(ipv6_buf->srcaddr.uint16[4]), 8);
hdr_pos += 8;
/* 64 bits. The address is derived using context information
* and the 64 bits carried inline. */
lowpan_iphc[1] |= 0x10;
}
}
else {
/* full address carried inline */
memcpy(&ipv6_hdr_fields[hdr_pos], &(ipv6_buf->srcaddr.uint8[0]), 16);
hdr_pos += 16;
}
}
/* M: Multicast Compression */
if (ipv6_addr_is_multicast(&ipv6_buf->destaddr)) {
/* 1: Destination address is a multicast address. */
lowpan_iphc[1] |= SIXLOWPAN_IPHC2_M;
/* Check if we can compress any part of the address */
if ((ipv6_buf->destaddr.uint8[0] == 0xff) &&
(ipv6_buf->destaddr.uint16[1] == HTONS(0x0000u)) &&
(ipv6_buf->destaddr.uint32[1] == HTONL(0x00000000u)) &&
(ipv6_buf->destaddr.uint16[4] == HTONS(0x0000u)) &&
(ipv6_buf->destaddr.uint8[10] == 0x00)) {
/* If we got here then the address is on the form:
* FFXX:0000:0000:0000:0000:00XX:XXXX:XXXX
* which means that it can be compressed. */
if ((ipv6_buf->destaddr.uint8[1] == 0x02) &&
(ipv6_buf->destaddr.uint16[5] == HTONS(0x0000u)) &&
(ipv6_buf->destaddr.uint16[6] == HTONS(0x0000u)) &&
(ipv6_buf->destaddr.uint8[14] == 0x00)) {
/* 11: 8 bits. The address takes the form FF02::00XX. */
lowpan_iphc[1] |= 0x03;
ipv6_hdr_fields[hdr_pos] = ipv6_buf->destaddr.uint8[15];
hdr_pos++;
}
else if ((ipv6_buf->destaddr.uint16[5] == HTONS(0x0000u)) &&
(ipv6_buf->destaddr.uint8[12] == 0x00)) {
/* 10: 32 bits. The address takes the form FFXX::00XX:XXXX. */
lowpan_iphc[1] |= 0x02;
/* copy second and last 3 bytes */
ipv6_hdr_fields[hdr_pos] = ipv6_buf->destaddr.uint8[1];
hdr_pos++;
memcpy(&ipv6_hdr_fields[hdr_pos], &ipv6_buf->destaddr.uint8[13], 3);
hdr_pos += 3;
}
else {
/* 01: 48 bits. The address takes the form FFXX::00XX:XXXX:XXXX */
lowpan_iphc[1] |= 0x01;
/* copy second and last 5 byte */
ipv6_hdr_fields[hdr_pos] = ipv6_buf->destaddr.uint8[1];
hdr_pos++;
memcpy(&ipv6_hdr_fields[hdr_pos], &ipv6_buf->destaddr.uint8[11], 5);
hdr_pos += 5;
}
}
else {
/* Full 128 bit address */
memcpy(&ipv6_hdr_fields[hdr_pos], &ipv6_buf->destaddr.uint8[0], 16);
hdr_pos += 16;
}
}
else {
/* 0: Destination address is not a multicast address. */
if ((con = lowpan_context_lookup(&ipv6_buf->destaddr)) != NULL) {
/* 1: Destination address compression uses stateful, context-based
* compression. */
lowpan_iphc[1] |= SIXLOWPAN_IPHC2_DAC;
ipv6_hdr_fields[0] = con->num;
}
if (con || ipv6_addr_is_link_local(&ipv6_buf->destaddr)) {
if (dest_len == 8 &&
ipv6_buf->destaddr.uint8[8] == (dest[0] ^ 0x02) &&
memcmp(&ipv6_buf->destaddr.uint8[9], &dest[1], 7) == 0) {
/* 0 bits. The address is derived using context information
* and possibly the link-layer addresses.*/
lowpan_iphc[1] |= 0x03;
}
else if (ipv6_buf->destaddr.uint32[2] == HTONL(0x000000ff) &&
ipv6_buf->destaddr.uint16[6] == HTONS(0xfe00)) {
if (dest_len == 2 &&
ipv6_buf->destaddr.uint8[14] == dest[0] &&
ipv6_buf->destaddr.uint8[15] == dest[1]) {
/* 0 bits. The address is derived using context information
* and possibly the link-layer addresses.*/
lowpan_iphc[1] |= 0x03;
}
else {
/* The 48 leading bits of the interface identifier are
* 0000:00FF:FE00, so we can compress the source address-iid to
* 16-bit */
memcpy(&ipv6_hdr_fields[hdr_pos], &ipv6_buf->destaddr.uint16[7], 2);
hdr_pos += 2;
/* 16 bits. The address is derived using context information
* and the 16 bits carried inline. */
lowpan_iphc[1] |= 0x02;
}
}
else {
memcpy(&ipv6_hdr_fields[hdr_pos], &(ipv6_buf->destaddr.uint16[4]), 8);
hdr_pos += 8;
/* 64 bits. The address is derived using context information
* and the 64 bits carried inline. */
lowpan_iphc[1] |= 0x01;
}
}
else {
/* Full 128 bit address */
memcpy(&ipv6_hdr_fields[hdr_pos], &(ipv6_buf->destaddr.uint8[0]), 16);
hdr_pos += 16;
}
}
mutex_unlock(&lowpan_context_mutex);
comp_buf[0] = lowpan_iphc[0];
comp_buf[1] = lowpan_iphc[1];
/*uint8_t *ptr;
if (ipv6_buf->nextheader == IPV6_PROTO_NUM_TCP)
{
ptr = get_payload_buf_send(ipv6_ext_hdr_len);
}
else
{
ptr = get_payload_buf(ipv6_ext_hdr_len);
}
*/
memcpy(&ipv6_hdr_fields[hdr_pos], &ptr[IPV6_HDR_LEN], NTOHS(ipv6_buf->length));
comp_len = 2 + hdr_pos + payload_length;
return 1;
}
void lowpan_iphc_decoding(uint8_t *data, uint8_t length, net_if_eui64_t *s_addr,
net_if_eui64_t *d_addr)
{
uint8_t hdr_pos = 0;
uint8_t *ipv6_hdr_fields = data;
uint8_t lowpan_iphc[2];
uint8_t cid = 0;
uint8_t dci = 0;
uint8_t ll_prefix[2] = {0xfe, 0x80};
lowpan_context_t *con = NULL;
ipv6_buf = ipv6_get_buf();
lowpan_iphc[0] = ipv6_hdr_fields[0];
lowpan_iphc[1] = ipv6_hdr_fields[1];
hdr_pos += 2;
/* first check if CID flag is set */
if (lowpan_iphc[1] & SIXLOWPAN_IPHC2_CID) {
hdr_pos++;
cid = 1;
}
/* TF: Traffic Class, Flow Label: */
if (lowpan_iphc[0] & SIXLOWPAN_IPHC1_FL_C) {
/* flowlabel is elided */
if (lowpan_iphc[0] & SIXLOWPAN_IPHC1_TC_C) {
/* traffic class is elided */
ipv6_buf->version_trafficclass = 0x60;
ipv6_buf->trafficclass_flowlabel = 0;
ipv6_buf->flowlabel = 0;
}
else {
/* toogle ecn/dscp order */
ipv6_buf->version_trafficclass = 0x60 | (0x0f &
(ipv6_hdr_fields[hdr_pos] >> 2));
ipv6_buf->trafficclass_flowlabel = ((ipv6_hdr_fields[hdr_pos] >> 2) & 0x30) |
((ipv6_hdr_fields[hdr_pos] << 6) & 0xc0);
ipv6_buf->flowlabel = 0;
hdr_pos += 3;
}
}
else {
/* flowlabel carried inline */
if (lowpan_iphc[0] & SIXLOWPAN_IPHC1_TC_C) {
/* traffic class is elided */
ipv6_buf->version_trafficclass = 0x60;
/* ecn + 4 bit flowlabel*/
ipv6_buf->trafficclass_flowlabel = ((ipv6_hdr_fields[hdr_pos] >> 2) & 0x30) |
(ipv6_hdr_fields[hdr_pos] & 0x0f);
hdr_pos++;
/* copy 2byte flowlabel */
memcpy(&ipv6_buf->flowlabel, &ipv6_hdr_fields[hdr_pos], 2);
hdr_pos += 2;
}
else {
ipv6_buf->version_trafficclass = 0x60 | (0x0f &
(ipv6_hdr_fields[hdr_pos] >> 2));
ipv6_buf->trafficclass_flowlabel = ((ipv6_hdr_fields[hdr_pos] >> 2) & 0x30) |
(ipv6_hdr_fields[hdr_pos] & 0x0f) |
(ipv6_hdr_fields[hdr_pos + 1] & 0x0f);
hdr_pos += 2;
memcpy(&ipv6_buf->trafficclass_flowlabel,
&ipv6_hdr_fields[hdr_pos], 2);
hdr_pos += 2;
}
}
/* NH: Next Header: */
if (!(lowpan_iphc[0] & SIXLOWPAN_IPHC1_NH)) {
ipv6_buf->nextheader = ipv6_hdr_fields[hdr_pos];
hdr_pos++;
}
/* HLIM: Hop Limit: */
if (lowpan_iphc[0] & 0x03) {
switch (lowpan_iphc[0] & 0x03) {
case (0x01): {
ipv6_buf->hoplimit = 1;
break;
}
case (0x02): {
ipv6_buf->hoplimit = 64;
break;
}
case (0x03): {
ipv6_buf->hoplimit = 255;
break;
}
default:
break;
}
}
else {
ipv6_buf->hoplimit = ipv6_hdr_fields[hdr_pos];
hdr_pos++;
}
/* Generate IPv6 address from stateless information, if SAC=1 we will use
* this stateless information as a starting point for the stateful
* information. */
/* RFC 6282 describes each of the following address compression methods */
switch (((lowpan_iphc[1] & SIXLOWPAN_IPHC2_SAM) >> 4) & 0x03) {
case (0x01): {
/* 64-bits */
memcpy(&(ipv6_buf->srcaddr.uint8[0]), &ll_prefix[0], 2);
memset(&(ipv6_buf->srcaddr.uint8[2]), 0, 6);
memcpy(&(ipv6_buf->srcaddr.uint8[8]), &ipv6_hdr_fields[hdr_pos], 8);
hdr_pos += 8;
break;
}
case (0x02): {
/* 16-bits */
memcpy(&(ipv6_buf->srcaddr.uint8[0]), &ll_prefix[0], 2);
ipv6_buf->srcaddr.uint16[1] = HTONS(0x0000u);
ipv6_buf->srcaddr.uint32[1] = HTONL(0x00000000u);
ipv6_buf->srcaddr.uint32[2] = HTONL(0x000000ffu);
ipv6_buf->srcaddr.uint16[6] = HTONS(0xfe00u);
memcpy(&(ipv6_buf->srcaddr.uint8[14]), &ipv6_hdr_fields[hdr_pos], 2);
hdr_pos += 2;
break;
}
case (0x03): {
/* 0-bits */
memcpy(&(ipv6_buf->srcaddr.uint8[0]), &ll_prefix[0], 2);
memset(&(ipv6_buf->srcaddr.uint8[2]), 0, 6);
memcpy(&(ipv6_buf->srcaddr.uint8[8]), &s_addr->uint8[0], 8);
/* Invert Universal/local bit as specified in
* RFC4291, section 2.5.1 "Interface Identifiers" */
ipv6_buf->srcaddr.uint8[8] ^= 0x02;
break;
}
default: {
/* full address carried inline */
memcpy(&(ipv6_buf->srcaddr.uint8[0]),
&ipv6_hdr_fields[hdr_pos], 16);
hdr_pos += 16;
break;
}
}
/* CID: Context Identifier Extension: + SAC: Source Address Compression */
if (lowpan_iphc[1] & SIXLOWPAN_IPHC2_SAC) {
/* 1: Source address compression uses stateful, context-based
* compression.*/
uint8_t sci = 0;
if (cid) {
sci = ipv6_hdr_fields[3] >> 4;
}
mutex_lock(&lowpan_context_mutex);
/* check context number */
if (((lowpan_iphc[1] & SIXLOWPAN_IPHC2_SAM) >> 4) & 0x03) {
con = lowpan_context_num_lookup(sci);
}
if (con == NULL) {
printf("ERROR: context not found\n");
mutex_unlock(&lowpan_context_mutex);
return;
}
/* By RFC 6282 3.1.1. Bits covered by context
* information are always used. */
memcpy(&(ipv6_buf->srcaddr.uint8[0]), &con->prefix, con->length);
mutex_unlock(&lowpan_context_mutex);
}
/* M: Multicast Compression + DAC: Destination Address Compression */
if (lowpan_iphc[1] & SIXLOWPAN_IPHC2_M) {
/* 1: Destination address is a multicast address. */
if (lowpan_iphc[1] & SIXLOWPAN_IPHC2_DAC) {
/* 1: Destination address compression uses stateful, context-based
* compression.
* If M=1 and DAC=1: */
if (cid) {
dci = ipv6_hdr_fields[3] & 0x0f;
}
mutex_lock(&lowpan_context_mutex);
if ((lowpan_iphc[1] & SIXLOWPAN_IPHC2_DAM) & 0x03) {
con = lowpan_context_num_lookup(dci);
}
if (con == NULL) {
printf("ERROR: context not found\n");
mutex_unlock(&lowpan_context_mutex);
return;
}
// TODO:
mutex_unlock(&lowpan_context_mutex);
}
else {
uint8_t m_prefix[2] = {0xff, 0x02};
/* If M=1 and DAC=0: */
switch (lowpan_iphc[1] & SIXLOWPAN_IPHC2_DAM) {
case (0x01): {
m_prefix[1] = ipv6_hdr_fields[hdr_pos];
hdr_pos++;
memcpy(&(ipv6_buf->destaddr.uint8[0]), &m_prefix[0], 2);
memset(&(ipv6_buf->destaddr.uint8[2]), 0, 9);
memcpy(&(ipv6_buf->destaddr.uint8[11]), &ipv6_hdr_fields[hdr_pos], 5);
hdr_pos += 5;
break;
}
case (0x02): {
m_prefix[1] = ipv6_hdr_fields[hdr_pos];
hdr_pos++;
memcpy(&(ipv6_buf->destaddr.uint8[0]), &m_prefix[0], 2);
memset(&(ipv6_buf->destaddr.uint8[2]), 0, 11);
memcpy(&(ipv6_buf->destaddr.uint8[13]), &ipv6_hdr_fields[hdr_pos], 3);
hdr_pos += 3;
break;
}
case (0x03): {
memcpy(&(ipv6_buf->destaddr.uint8[0]), &m_prefix[0], 2);
memset(&(ipv6_buf->destaddr.uint8[2]), 0, 13);
memcpy(&(ipv6_buf->destaddr.uint8[15]), &ipv6_hdr_fields[hdr_pos], 1);
hdr_pos++;
break;
}
default: {
memcpy(&(ipv6_buf->destaddr.uint8[0]), &ipv6_hdr_fields[hdr_pos], 16);
hdr_pos += 16;
break;
}
}
}
}
else {
switch ((lowpan_iphc[1] & SIXLOWPAN_IPHC2_DAM) & 0x03) {
case (0x01): {
/* 64-bits */
memcpy(&(ipv6_buf->destaddr.uint8[0]), &ll_prefix[0], 2);
memset(&(ipv6_buf->destaddr.uint8[2]), 0, 6);
memcpy(&(ipv6_buf->destaddr.uint8[8]), &ipv6_hdr_fields[hdr_pos], 8);
hdr_pos += 8;
break;
}
case (0x02): {
/* 16-bits */
memcpy(&(ipv6_buf->destaddr.uint8[0]), &ll_prefix[0], 2);
ipv6_buf->destaddr.uint16[1] = HTONS(0x0000u);
ipv6_buf->destaddr.uint32[1] = HTONL(0x00000000u);
ipv6_buf->destaddr.uint32[2] = HTONL(0x000000ffu);
ipv6_buf->destaddr.uint16[6] = HTONS(0xfe00u);
memcpy(&(ipv6_buf->destaddr.uint8[14]), &ipv6_hdr_fields[hdr_pos], 2);
hdr_pos += 2;
break;
}
case (0x03): {
/* 0-bits */
memcpy(&(ipv6_buf->destaddr.uint8[0]), &ll_prefix[0], 2);
memset(&(ipv6_buf->destaddr.uint8[2]), 0, 6);
memcpy(&(ipv6_buf->destaddr.uint8[8]), &d_addr->uint8[0], 8);
/* Invert Universal/local bit as specified in
* RFC4291, section 2.5.1 "Interface Identifiers" */
ipv6_buf->destaddr.uint8[8] ^= 0x02;
break;
}
default: {
/* full address carried inline */
memcpy(&(ipv6_buf->destaddr.uint8[0]),
&ipv6_hdr_fields[hdr_pos], 16);
hdr_pos += 16;
break;
}
}
if (lowpan_iphc[1] & SIXLOWPAN_IPHC2_DAC) {
/* 1: Destination address compression uses stateful, context-based
* compression.
* If M=0 and DAC=1: */
if (cid) {
dci = ipv6_hdr_fields[3] & 0x0f;
}
mutex_lock(&lowpan_context_mutex);
if ((lowpan_iphc[1] & SIXLOWPAN_IPHC2_DAM) & 0x03) {
con = lowpan_context_num_lookup(dci);
}
if (con == NULL) {
printf("ERROR: context not found\n");
mutex_unlock(&lowpan_context_mutex);
return;
}
/* By RFC 6282 3.1.1. Bits covered by context
* information are always used. */
memcpy(&(ipv6_buf->destaddr.uint8[0]), &con->prefix, con->length);
mutex_unlock(&lowpan_context_mutex);
}
}
uint8_t *ptr = get_payload_buf(ipv6_ext_hdr_len);
if (lowpan_iphc[0] & SIXLOWPAN_IPHC1_NH) {
if ((ipv6_hdr_fields[hdr_pos] & SIXLOWPAN_NHC_UDP_MASK) == SIXLOWPAN_NHC_UDP_ID) {
ipv6_buf->nextheader = IPV6_PROTO_NUM_UDP;
/* ipv6 length */
ipv6_buf->length = HTONS(length - hdr_pos + 1);
packet_length = IPV6_HDR_LEN + ipv6_buf->length;
hdr_pos ++;
/* copy inline src_port and dst_port */
memcpy(ptr, &ipv6_hdr_fields[hdr_pos], 4);
hdr_pos += 4;
ptr += 4;
/* insert length value */
*((uint16_t*)ptr) = ipv6_buf->length;
ptr += 2;
}
}
else {
/* ipv6 length */
ipv6_buf->length = HTONS(length - hdr_pos);
packet_length = IPV6_HDR_LEN + ipv6_buf->length;
}
memcpy(ptr, &ipv6_hdr_fields[hdr_pos], length - hdr_pos);
}
uint8_t lowpan_context_len(void)
{
return context_len;
}
void lowpan_context_remove(uint8_t num)
{
int i, j;
for (i = 0; i < NDP_6LOWPAN_CONTEXT_MAX; i++) {
if (contexts[i].num == num) {
context_len--;
break;
}
}
abr_remove_context(num);
for (j = i; j < (NDP_6LOWPAN_CONTEXT_MAX - 1); j++) {
contexts[j] = contexts[j + 1];
}
memset(&contexts[NDP_6LOWPAN_CONTEXT_MAX - 1], 0, sizeof(lowpan_context_t));
}
lowpan_context_t *lowpan_context_update(uint8_t num, const ipv6_addr_t *prefix,
uint8_t length, uint8_t comp,
uint16_t lifetime)
{
lowpan_context_t *context;
if (lifetime == 0) {
lowpan_context_remove(num);
return NULL;
}
if (context_len == NDP_6LOWPAN_CONTEXT_MAX) {
return NULL;
}
context = lowpan_context_num_lookup(num);
if (context == NULL) {
context = &(contexts[context_len++]);
}
context->num = num;
memset((void *)(&context->prefix), 0, 16);
// length in bits
memcpy((void *)(&context->prefix), (void *)prefix, length / 8);
context->length = length;
context->comp = comp;
context->lifetime = lifetime;
return context;
}
lowpan_context_t *lowpan_context_get(void)
{
return contexts;
}
lowpan_context_t *lowpan_context_lookup(ipv6_addr_t *addr)
{
int i;
lowpan_context_t *context = NULL;
for (i = 0; i < lowpan_context_len(); i++) {
if (contexts[i].length > 0 && memcmp((void *)addr, &(contexts[i].prefix),
contexts[i].length) == 0) {
/* longer prefixes are always prefered */
if (context == NULL || context->length < contexts[i].length) {
context = &contexts[i];
}
}
}
return context;
}
lowpan_context_t *lowpan_context_num_lookup(uint8_t num)
{
int i;
for (i = 0; i < lowpan_context_len(); i++) {
if (contexts[i].num == num) {
return &contexts[i];
}
}
return NULL;
}
static void *lowpan_context_auto_remove(void *arg)
{
(void) arg;
timex_t minute = timex_set(60, 0);
int i;
int8_t to_remove[NDP_6LOWPAN_CONTEXT_MAX];
while (1) {
vtimer_sleep(minute);
int8_t to_remove_size = 0;
mutex_lock(&lowpan_context_mutex);
for (i = 0; i < lowpan_context_len(); i++) {
if (--(contexts[i].lifetime) == 0) {
to_remove[to_remove_size++] = contexts[i].num;
}
}
for (i = 0; i < to_remove_size; i++) {
lowpan_context_remove(to_remove[i]);
}
mutex_unlock(&lowpan_context_mutex);
}
return NULL;
}
void init_reas_bufs(lowpan_reas_buf_t *buf)
{
memset(&buf->s_addr, 0, sizeof(net_if_eui64_t));
memset(&buf->d_addr, 0, sizeof(net_if_eui64_t));
buf->tag = 0;
memset(&buf->timestamp, 0, sizeof(timex_t));
buf->packet_size = 0;
buf->current_packet_size = 0;
buf->packet = NULL;
buf->interval_list_head = NULL;
buf->next = NULL;
}
int sixlowpan_lowpan_init_adhoc_interface(int if_id, const ipv6_addr_t *prefix)
{
ipv6_addr_t tmp;
/* if prefix is set */
if (prefix != NULL) {
/* init network prefix */
ipv6_addr_t save_prefix;
ipv6_addr_init_prefix(&save_prefix, prefix, 64);
ndp_add_prefix_info(if_id, &save_prefix, 64,
NDP_OPT_PI_VLIFETIME_INFINITE,
NDP_OPT_PI_PLIFETIME_INFINITE, 1,
ICMPV6_NDP_OPT_PI_FLAG_AUTONOM);
ipv6_init_as_router();
/* add global address */
if (!ipv6_addr_set_by_eui64(&tmp, if_id, prefix)) {
return 0;
}
DEBUG("%s, %d: set unique address for interface %d to %s, according "
"to prefix %s\n", __FILE__, __LINE__, if_id,
ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &tmp),
ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &tmp));
if (!ipv6_net_if_add_addr(if_id, &tmp, NDP_ADDR_STATE_PREFERRED,
NDP_OPT_PI_VLIFETIME_INFINITE,
NDP_OPT_PI_PLIFETIME_INFINITE, 0)) {
return 0;
}
}
return sixlowpan_lowpan_init_interface(if_id);
}
int sixlowpan_lowpan_init_interface(int if_id)
{
ipv6_addr_t tmp;
/* init link-local prefix */
ipv6_addr_set_link_local_prefix(&tmp);
if (!ipv6_addr_set_by_eui64(&tmp, if_id, &tmp)) {
DEBUG("Can not set link-local by EUI-64 on interface %d\n", if_id);
return 0;
}
DEBUG("%s, %d: sixlowpan_lowpan_init(): add link local address to "
"interface %d: %s\n", __FILE__, __LINE__, if_id,
ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &tmp));
if (!ipv6_net_if_add_addr(if_id, &tmp, NDP_ADDR_STATE_PREFERRED,
NDP_OPT_PI_VLIFETIME_INFINITE,
NDP_OPT_PI_PLIFETIME_INFINITE, 0)) {
DEBUG("Can not add link-local address to interface %d\n", if_id);
return 0;
}
/* add solicited nodes multicast address of link local address */
ipv6_addr_set_solicited_node_addr(&tmp, &tmp);
DEBUG("%s, %d: sixlowpan_lowpan_init(): add solicited nodes multicast address "
"to of link layer address interface %d: %s\n", __FILE__, __LINE__,
if_id, ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &tmp));
if (!ipv6_net_if_add_addr(if_id, &tmp, NDP_ADDR_STATE_PREFERRED,
NDP_OPT_PI_VLIFETIME_INFINITE,
NDP_OPT_PI_PLIFETIME_INFINITE, 0)) {
DEBUG("Can not add all nodes address to interface %d\n", if_id);
return 0;
}
/* add all nodes multicast address */
ipv6_addr_set_all_nodes_addr(&tmp);
DEBUG("%s, %d: sixlowpan_lowpan_init(): add all nodes multicast address "
"to interface %d: %s\n", __FILE__, __LINE__, if_id,
ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &tmp));
if (!ipv6_net_if_add_addr(if_id, &tmp, NDP_ADDR_STATE_PREFERRED,
NDP_OPT_PI_VLIFETIME_INFINITE,
NDP_OPT_PI_PLIFETIME_INFINITE, 0)) {
DEBUG("Can not add all nodes address to interface %d\n", if_id);
return 0;
}
/* add loopback address */
ipv6_addr_set_loopback_addr(&tmp);
DEBUG("%s, %d: sixlowpan_lowpan_init(): add loopback address to interface "
"0: %s\n", __FILE__, __LINE__,
ipv6_addr_to_str(addr_str, IPV6_MAX_ADDR_STR_LEN, &tmp));
if (!ipv6_net_if_add_addr(0, &tmp, NDP_ADDR_STATE_PREFERRED,
NDP_OPT_PI_VLIFETIME_INFINITE,
NDP_OPT_PI_PLIFETIME_INFINITE, 0)) {
DEBUG("Can not add all nodes address to interface %d\n", if_id);
return 0;
}
icmpv6_send_router_sol(OPT_SLLAO);
return 1;
}
int sixlowpan_lowpan_init(void)
{
short i;
/* init mac-layer and radio transceiver */
sixlowpan_mac_init();
if (ip_process_pid == KERNEL_PID_UNDEF) {
ip_process_pid = thread_create(ip_process_buf, IP_PROCESS_STACKSIZE,
PRIORITY_MAIN - 1, CREATE_STACKTEST,
ipv6_process, NULL, "ip_process");
}
if (ip_process_pid == KERNEL_PID_UNDEF) {
return 0;
}
nbr_cache_auto_rem();
contexts_rem_pid = thread_create(con_buf, CON_STACKSIZE,
PRIORITY_MAIN + 1, CREATE_STACKTEST,
lowpan_context_auto_remove, NULL, "lowpan_context_rem");
if (contexts_rem_pid == KERNEL_PID_UNDEF) {
return 0;
}
transfer_pid = thread_create(lowpan_transfer_buf, LOWPAN_TRANSFER_BUF_STACKSIZE,
PRIORITY_MAIN - 1, CREATE_STACKTEST,
lowpan_transfer, NULL, "lowpan_transfer");
if (transfer_pid == KERNEL_PID_UNDEF) {
return 0;
}
for (i = 0; i < SIXLOWIP_MAX_REGISTERED; i++) {
sixlowip_reg[i] = 0;
}
return 0;
}
void sixlowpan_lowpan_bootstrapping(void)
{
icmpv6_send_router_sol(OPT_SLLAO);
}