1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c

798 lines
26 KiB
C
Raw Normal View History

2015-03-03 22:20:21 +01:00
/*
* Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de>
*
* 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.
*/
/**
* @{
*
* @file
*/
#include <assert.h>
2015-03-03 22:20:21 +01:00
#include <errno.h>
#include <inttypes.h>
#include <stdbool.h>
#include "byteorder.h"
#include "cpu_conf.h"
2015-03-03 22:20:21 +01:00
#include "kernel_types.h"
2015-08-10 02:41:08 +02:00
#include "net/gnrc.h"
2015-08-28 04:04:00 +02:00
#include "net/gnrc/icmpv6.h"
#include "net/gnrc/sixlowpan/ctx.h"
#include "net/gnrc/sixlowpan/nd.h"
2015-08-07 15:08:53 +02:00
#include "net/protnum.h"
2015-03-03 22:20:21 +01:00
#include "thread.h"
#include "utlist.h"
2017-05-22 15:52:11 +02:00
#include "net/gnrc/ipv6/nib.h"
2017-11-16 18:06:46 +01:00
#include "net/gnrc/netif/internal.h"
2015-09-28 14:40:35 +02:00
#include "net/gnrc/ipv6/whitelist.h"
#include "net/gnrc/ipv6/blacklist.h"
2015-03-03 22:20:21 +01:00
#include "net/gnrc/ipv6.h"
2015-03-03 22:20:21 +01:00
#define ENABLE_DEBUG (0)
#include "debug.h"
#define _MAX_L2_ADDR_LEN (8U)
#if ENABLE_DEBUG
static char _stack[GNRC_IPV6_STACK_SIZE + THREAD_EXTRA_STACKSIZE_PRINTF];
#else
static char _stack[GNRC_IPV6_STACK_SIZE];
#endif
2015-03-03 22:20:21 +01:00
#ifdef MODULE_FIB
#include "net/fib.h"
#include "net/fib/table.h"
/**
* @brief buffer to store the entries in the IPv6 forwarding table
*/
static fib_entry_t _fib_entries[GNRC_IPV6_FIB_TABLE_SIZE];
/**
* @brief the IPv6 forwarding table
*/
fib_table_t gnrc_ipv6_fib_table;
#endif
2015-08-10 00:26:36 +02:00
static char addr_str[IPV6_ADDR_MAX_STR_LEN];
2015-03-03 22:20:21 +01:00
kernel_pid_t gnrc_ipv6_pid = KERNEL_PID_UNDEF;
/* handles GNRC_NETAPI_MSG_TYPE_RCV commands */
static void _receive(gnrc_pktsnip_t *pkt);
2015-03-03 22:20:21 +01:00
/* Sends packet over the appropriate interface(s).
* prep_hdr: prepare header for sending (call to _fill_ipv6_hdr()), otherwise
* assume it is already prepared */
static void _send(gnrc_pktsnip_t *pkt, bool prep_hdr);
2015-03-03 22:20:21 +01:00
/* Main event loop for IPv6 */
static void *_event_loop(void *args);
kernel_pid_t gnrc_ipv6_init(void)
2015-03-03 22:20:21 +01:00
{
if (gnrc_ipv6_pid == KERNEL_PID_UNDEF) {
gnrc_ipv6_pid = thread_create(_stack, sizeof(_stack), GNRC_IPV6_PRIO,
2015-12-02 12:00:19 +01:00
THREAD_CREATE_STACKTEST,
_event_loop, NULL, "ipv6");
2015-03-03 22:20:21 +01:00
}
#ifdef MODULE_FIB
2015-08-05 14:40:49 +02:00
gnrc_ipv6_fib_table.data.entries = _fib_entries;
gnrc_ipv6_fib_table.table_type = FIB_TABLE_TYPE_SH;
gnrc_ipv6_fib_table.size = GNRC_IPV6_FIB_TABLE_SIZE;
fib_init(&gnrc_ipv6_fib_table);
#endif
return gnrc_ipv6_pid;
2015-03-03 22:20:21 +01:00
}
static void _dispatch_next_header(gnrc_pktsnip_t *pkt, unsigned nh,
bool interested);
2016-04-09 22:11:21 +02:00
static inline bool _gnrc_ipv6_is_interested(unsigned nh) {
#ifdef MODULE_GNRC_ICMPV6
return (nh == PROTNUM_ICMPV6);
#else /* MODULE_GNRC_ICMPV6 */
return false;
#endif /* MODULE_GNRC_ICMPV6 */
}
static void _demux(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt, unsigned nh)
{
pkt->type = gnrc_nettype_from_protnum(nh);
_dispatch_next_header(pkt, nh, _gnrc_ipv6_is_interested(nh));
switch (nh) {
#ifdef MODULE_GNRC_ICMPV6
2016-04-09 22:11:21 +02:00
case PROTNUM_ICMPV6:
DEBUG("ipv6: handle ICMPv6 packet (nh = %u)\n", nh);
gnrc_icmpv6_demux(netif, pkt);
break;
#endif /* MODULE_GNRC_ICMPV6 */
2016-04-09 22:11:21 +02:00
default:
break;
}
2015-03-03 22:20:21 +01:00
}
ipv6_hdr_t *gnrc_ipv6_get_header(gnrc_pktsnip_t *pkt)
{
gnrc_pktsnip_t *tmp = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6);
if (tmp == NULL) {
return NULL;
}
assert(tmp->data != NULL);
assert(tmp->size >= sizeof(ipv6_hdr_t));
assert(ipv6_hdr_is(tmp->data));
return ((ipv6_hdr_t*) tmp->data);
}
2015-03-03 22:20:21 +01:00
/* internal functions */
static void _dispatch_next_header(gnrc_pktsnip_t *pkt, unsigned nh,
bool interested)
2016-04-09 22:11:21 +02:00
{
const bool has_nh_subs = (gnrc_netreg_num(GNRC_NETTYPE_IPV6, nh) > 0) ||
interested;
2016-04-09 22:11:21 +02:00
DEBUG("ipv6: forward nh = %u to other threads\n", nh);
if (has_nh_subs) {
gnrc_pktbuf_hold(pkt, 1); /* don't remove from packet buffer in
* next dispatch */
}
if (gnrc_netapi_dispatch_receive(pkt->type,
GNRC_NETREG_DEMUX_CTX_ALL,
pkt) == 0) {
gnrc_pktbuf_release(pkt);
}
if (!has_nh_subs) {
/* we should exit early. pkt was already released above */
return;
2016-04-09 22:11:21 +02:00
}
if (interested) {
gnrc_pktbuf_hold(pkt, 1); /* don't remove from packet buffer in
* next dispatch */
}
if (gnrc_netapi_dispatch_receive(GNRC_NETTYPE_IPV6, nh, pkt) == 0) {
gnrc_pktbuf_release(pkt);
}
}
2015-03-03 22:20:21 +01:00
static void *_event_loop(void *args)
{
msg_t msg, reply, msg_q[GNRC_IPV6_MSG_QUEUE_SIZE];
gnrc_netreg_entry_t me_reg = GNRC_NETREG_ENTRY_INIT_PID(GNRC_NETREG_DEMUX_CTX_ALL,
sched_active_pid);
2015-03-03 22:20:21 +01:00
(void)args;
msg_init_queue(msg_q, GNRC_IPV6_MSG_QUEUE_SIZE);
2015-03-03 22:20:21 +01:00
/* register interest in all IPv6 packets */
gnrc_netreg_register(GNRC_NETTYPE_IPV6, &me_reg);
2015-03-03 22:20:21 +01:00
/* preinitialize ACK */
reply.type = GNRC_NETAPI_MSG_TYPE_ACK;
2015-03-03 22:20:21 +01:00
/* start event loop */
while (1) {
DEBUG("ipv6: waiting for incoming message.\n");
msg_receive(&msg);
switch (msg.type) {
case GNRC_NETAPI_MSG_TYPE_RCV:
DEBUG("ipv6: GNRC_NETAPI_MSG_TYPE_RCV received\n");
_receive(msg.content.ptr);
2015-03-03 22:20:21 +01:00
break;
case GNRC_NETAPI_MSG_TYPE_SND:
DEBUG("ipv6: GNRC_NETAPI_MSG_TYPE_SND received\n");
_send(msg.content.ptr, true);
2015-03-03 22:20:21 +01:00
break;
case GNRC_NETAPI_MSG_TYPE_GET:
case GNRC_NETAPI_MSG_TYPE_SET:
2015-03-03 22:20:21 +01:00
DEBUG("ipv6: reply to unsupported get/set\n");
reply.content.value = -ENOTSUP;
msg_reply(&msg, &reply);
break;
2017-05-22 15:52:11 +02:00
case GNRC_IPV6_NIB_SND_UC_NS:
case GNRC_IPV6_NIB_SND_MC_NS:
case GNRC_IPV6_NIB_SND_NA:
case GNRC_IPV6_NIB_SEARCH_RTR:
case GNRC_IPV6_NIB_REPLY_RS:
case GNRC_IPV6_NIB_SND_MC_RA:
case GNRC_IPV6_NIB_REACH_TIMEOUT:
case GNRC_IPV6_NIB_DELAY_TIMEOUT:
case GNRC_IPV6_NIB_ADDR_REG_TIMEOUT:
case GNRC_IPV6_NIB_ABR_TIMEOUT:
case GNRC_IPV6_NIB_PFX_TIMEOUT:
case GNRC_IPV6_NIB_RTR_TIMEOUT:
case GNRC_IPV6_NIB_RECALC_REACH_TIME:
case GNRC_IPV6_NIB_REREG_ADDRESS:
case GNRC_IPV6_NIB_DAD:
case GNRC_IPV6_NIB_VALID_ADDR:
2017-05-22 15:52:11 +02:00
DEBUG("ipv6: NIB timer event received\n");
gnrc_ipv6_nib_handle_timer_event(msg.content.ptr, msg.type);
break;
2015-03-03 22:20:21 +01:00
default:
break;
}
}
return NULL;
}
2017-11-16 18:06:46 +01:00
static void _send_to_iface(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
2015-03-16 17:52:19 +01:00
{
2019-01-15 13:17:54 +01:00
const ipv6_hdr_t *hdr = pkt->next->data;
assert(netif != NULL);
((gnrc_netif_hdr_t *)pkt->data)->if_pid = netif->pid;
if (gnrc_pkt_len(pkt->next) > netif->ipv6.mtu) {
DEBUG("ipv6: packet too big\n");
gnrc_icmpv6_error_pkt_too_big_send(netif->ipv6.mtu, pkt);
gnrc_pktbuf_release_error(pkt, EMSGSIZE);
return;
}
2019-01-15 13:17:54 +01:00
DEBUG("ipv6: Sending (src = %s, ",
ipv6_addr_to_str(addr_str, &hdr->src, sizeof(addr_str)));
DEBUG("dst = %s, next header = %u, length = %u)\n",
ipv6_addr_to_str(addr_str, &hdr->dst, sizeof(addr_str)), hdr->nh,
byteorder_ntohs(hdr->len));
#ifdef MODULE_NETSTATS_IPV6
netif->ipv6.stats.tx_success++;
netif->ipv6.stats.tx_bytes += gnrc_pkt_len(pkt->next);
#endif
#ifdef MODULE_GNRC_SIXLOWPAN
2017-11-16 18:06:46 +01:00
if (gnrc_netif_is_6ln(netif)) {
2015-03-16 17:52:19 +01:00
DEBUG("ipv6: send to 6LoWPAN instead\n");
if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_SIXLOWPAN, GNRC_NETREG_DEMUX_CTX_ALL, pkt)) {
2016-09-27 23:38:41 +02:00
DEBUG("ipv6: no 6LoWPAN thread found\n");
gnrc_pktbuf_release(pkt);
2015-03-16 17:52:19 +01:00
}
return;
2015-03-16 17:52:19 +01:00
}
#endif
if (gnrc_netapi_send(netif->pid, pkt) < 1) {
DEBUG("ipv6: unable to send packet\n");
gnrc_pktbuf_release(pkt);
}
2015-03-16 17:52:19 +01:00
}
static gnrc_pktsnip_t *_create_netif_hdr(uint8_t *dst_l2addr,
unsigned dst_l2addr_len,
gnrc_pktsnip_t *pkt,
uint8_t flags)
2015-03-03 22:20:21 +01:00
{
gnrc_pktsnip_t *netif_hdr = gnrc_netif_hdr_build(NULL, 0, dst_l2addr, dst_l2addr_len);
gnrc_netif_hdr_t *hdr;
2015-03-03 22:20:21 +01:00
if (netif_hdr == NULL) {
2015-03-03 22:20:21 +01:00
DEBUG("ipv6: error on interface header allocation, dropping packet\n");
gnrc_pktbuf_release(pkt);
return NULL;
}
hdr = netif_hdr->data;
/* previous netif header might have been allocated by some higher layer
* to provide some flags (provided to us via netif_flags). */
hdr->flags = flags;
2015-03-03 22:20:21 +01:00
/* add netif_hdr to front of the pkt list */
LL_PREPEND(pkt, netif_hdr);
2015-03-03 22:20:21 +01:00
return pkt;
}
static bool _is_ipv6_hdr(gnrc_pktsnip_t *hdr)
{
#ifdef MODULE_GNRC_IPV6_EXT
return (hdr->type == GNRC_NETTYPE_IPV6) ||
(hdr->type == GNRC_NETTYPE_IPV6_EXT);
#else
return (hdr->type == GNRC_NETTYPE_IPV6);
#endif
2015-03-03 22:20:21 +01:00
}
static int _fill_ipv6_hdr(gnrc_netif_t *netif, gnrc_pktsnip_t *ipv6)
2015-03-03 22:20:21 +01:00
{
int res;
ipv6_hdr_t *hdr = ipv6->data;
gnrc_pktsnip_t *payload, *prev;
2015-03-03 22:20:21 +01:00
hdr->len = byteorder_htons(gnrc_pkt_len(ipv6->next));
DEBUG("ipv6: set payload length to %u (network byteorder %04" PRIx16 ")\n",
(unsigned)byteorder_ntohs(hdr->len), hdr->len.u16);
2015-03-03 22:20:21 +01:00
/* check if e.g. extension header was not already marked */
2015-08-07 15:08:53 +02:00
if (hdr->nh == PROTNUM_RESERVED) {
2018-11-17 12:46:00 +01:00
if (ipv6->next == NULL) {
2015-08-07 15:08:53 +02:00
hdr->nh = PROTNUM_IPV6_NONXT;
2015-03-03 22:20:21 +01:00
}
2018-11-17 12:46:00 +01:00
else {
hdr->nh = gnrc_nettype_to_protnum(ipv6->next->type);
/* if still reserved: mark no next header */
if (hdr->nh == PROTNUM_RESERVED) {
hdr->nh = PROTNUM_IPV6_NONXT;
}
}
2015-03-03 22:20:21 +01:00
}
DEBUG("ipv6: set next header to %u\n", hdr->nh);
2015-03-03 22:20:21 +01:00
if (hdr->hl == 0) {
if (netif == NULL) {
2017-11-16 18:06:46 +01:00
hdr->hl = GNRC_NETIF_DEFAULT_HL;
2015-06-09 21:38:02 +02:00
}
else {
hdr->hl = netif->cur_hl;
2015-06-09 21:38:02 +02:00
}
2015-03-03 22:20:21 +01:00
}
2015-08-10 00:26:36 +02:00
if (ipv6_addr_is_unspecified(&hdr->src)) {
if (ipv6_addr_is_loopback(&hdr->dst)) {
ipv6_addr_set_loopback(&hdr->src);
2015-03-03 22:20:21 +01:00
}
2015-06-09 21:38:02 +02:00
else {
2017-11-16 18:06:46 +01:00
ipv6_addr_t *src = gnrc_netif_ipv6_addr_best_src(netif, &hdr->dst,
false);
2015-03-03 22:20:21 +01:00
2015-06-09 21:38:02 +02:00
if (src != NULL) {
DEBUG("ipv6: set packet source to %s\n",
2015-08-10 00:26:36 +02:00
ipv6_addr_to_str(addr_str, src, sizeof(addr_str)));
memcpy(&hdr->src, src, sizeof(ipv6_addr_t));
2015-06-09 21:38:02 +02:00
}
/* Otherwise leave unspecified */
}
2015-03-03 22:20:21 +01:00
}
DEBUG("ipv6: write protect up to payload to calculate checksum\n");
payload = ipv6;
prev = ipv6;
while (_is_ipv6_hdr(payload) && (payload->next != NULL)) {
/* IPv6 header itself was already write-protected in caller function,
* just write protect extension headers and payload header */
if ((payload = gnrc_pktbuf_start_write(payload->next)) == NULL) {
DEBUG("ipv6: unable to get write access to IPv6 extension or payload header\n");
/* packet duplicated to this point will be released by caller,
* original packet by other subscriber */
return -ENOMEM;
}
prev->next = payload;
prev = payload;
}
2015-03-03 22:20:21 +01:00
DEBUG("ipv6: calculate checksum for upper header.\n");
if ((res = gnrc_netreg_calc_csum(payload, ipv6)) < 0) {
2015-03-03 22:20:21 +01:00
if (res != -ENOENT) { /* if there is no checksum we are okay */
DEBUG("ipv6: checksum calculation failed.\n");
/* packet will be released by caller */
2015-03-03 22:20:21 +01:00
return res;
}
}
return 0;
}
static bool _safe_fill_ipv6_hdr(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt,
bool prep_hdr)
2015-03-03 22:20:21 +01:00
{
if (prep_hdr && (_fill_ipv6_hdr(netif, pkt) < 0)) {
/* error on filling up header */
gnrc_pktbuf_release(pkt);
return false;
}
return true;
}
/* functions for sending */
static void _send_unicast(gnrc_pktsnip_t *pkt, bool prep_hdr,
gnrc_netif_t *netif, ipv6_hdr_t *ipv6_hdr,
uint8_t netif_hdr_flags)
{
gnrc_ipv6_nib_nc_t nce;
DEBUG("ipv6: send unicast\n");
if (gnrc_ipv6_nib_get_next_hop_l2addr(&ipv6_hdr->dst, netif, pkt,
&nce) < 0) {
/* packet is released by NIB */
DEBUG("ipv6: no link-layer address or interface for next hop to %s",
ipv6_addr_to_str(addr_str, &ipv6_hdr->dst, sizeof(addr_str)));
return;
}
netif = gnrc_netif_get_by_pid(gnrc_ipv6_nib_nc_get_iface(&nce));
assert(netif != NULL);
if (_safe_fill_ipv6_hdr(netif, pkt, prep_hdr)) {
DEBUG("ipv6: add interface header to packet\n");
if ((pkt = _create_netif_hdr(nce.l2addr, nce.l2addr_len, pkt,
netif_hdr_flags)) == NULL) {
return;
}
DEBUG("ipv6: send unicast over interface %" PRIkernel_pid "\n",
netif->pid);
/* and send to interface */
#ifdef MODULE_NETSTATS_IPV6
netif->ipv6.stats.tx_unicast_count++;
#endif
_send_to_iface(netif, pkt);
}
}
static inline void _send_multicast_over_iface(gnrc_pktsnip_t *pkt,
gnrc_netif_t *netif,
uint8_t netif_hdr_flags)
{
if ((pkt = _create_netif_hdr(NULL, 0, pkt,
netif_hdr_flags |
GNRC_NETIF_HDR_FLAGS_MULTICAST)) == NULL) {
return;
}
DEBUG("ipv6: send multicast over interface %" PRIkernel_pid "\n", netif->pid);
#ifdef MODULE_NETSTATS_IPV6
netif->ipv6.stats.tx_mcast_count++;
#endif
2015-03-03 22:20:21 +01:00
/* and send to interface */
_send_to_iface(netif, pkt);
2015-03-03 22:20:21 +01:00
}
static void _send_multicast(gnrc_pktsnip_t *pkt, bool prep_hdr,
gnrc_netif_t *netif, uint8_t netif_hdr_flags)
2015-03-03 22:20:21 +01:00
{
size_t ifnum = 0;
if (netif == NULL) {
2017-11-16 18:06:46 +01:00
ifnum = gnrc_netif_numof();
2015-03-03 22:20:21 +01:00
/* throw away packet if no one is interested */
if (ifnum == 0) {
DEBUG("ipv6: no interfaces registered, dropping packet\n");
gnrc_pktbuf_release(pkt);
2015-03-03 22:20:21 +01:00
return;
}
}
2015-03-03 22:20:21 +01:00
#if GNRC_NETIF_NUMOF > 1
/* interface not given: send over all interfaces */
if (netif == NULL) {
2015-03-03 22:20:21 +01:00
/* send packet to link layer */
gnrc_pktbuf_hold(pkt, ifnum - 1);
2015-03-03 22:20:21 +01:00
2017-11-16 18:06:46 +01:00
while ((netif = gnrc_netif_iter(netif))) {
2015-03-03 22:20:21 +01:00
if (prep_hdr) {
DEBUG("ipv6: prepare IPv6 header for sending\n");
2015-03-03 22:20:21 +01:00
/* need to get second write access (duplication) to fill IPv6
* header interface-local */
gnrc_pktsnip_t *tmp = gnrc_pktbuf_start_write(pkt);
2015-03-03 22:20:21 +01:00
if (tmp == NULL) {
2015-03-03 22:20:21 +01:00
DEBUG("ipv6: unable to get write access to IPv6 header, "
"for interface %" PRIkernel_pid "\n", netif->pid);
gnrc_pktbuf_release(pkt);
2015-03-03 22:20:21 +01:00
return;
}
if (_fill_ipv6_hdr(netif, tmp) < 0) {
2015-03-03 22:20:21 +01:00
/* error on filling up header */
if (tmp != pkt) {
gnrc_pktbuf_release(tmp);
}
gnrc_pktbuf_release(pkt);
2015-03-03 22:20:21 +01:00
return;
}
}
_send_multicast_over_iface(pkt, netif, netif_hdr_flags);
2015-03-03 22:20:21 +01:00
}
}
else {
if (_safe_fill_ipv6_hdr(netif, pkt, prep_hdr)) {
_send_multicast_over_iface(pkt, netif, netif_hdr_flags);
2015-03-03 22:20:21 +01:00
}
}
#else /* GNRC_NETIF_NUMOF */
(void)ifnum; /* not used in this build branch */
if (netif == NULL) {
2017-11-16 18:06:46 +01:00
netif = gnrc_netif_iter(NULL);
/* allocate interface header */
if ((pkt = _create_netif_hdr(NULL, 0, pkt, netif_hdr_flags)) == NULL) {
return;
}
}
if (_safe_fill_ipv6_hdr(netif, pkt, prep_hdr)) {
_send_multicast_over_iface(pkt, netif, netif_hdr_flags);
}
#endif /* GNRC_NETIF_NUMOF */
}
static void _send_to_self(gnrc_pktsnip_t *pkt, bool prep_hdr,
gnrc_netif_t *netif)
{
if (!_safe_fill_ipv6_hdr(netif, pkt, prep_hdr) ||
/* no netif header so we just merge the whole packet. */
(gnrc_pktbuf_merge(pkt) != 0)) {
DEBUG("ipv6: error looping packet to sender.\n");
gnrc_pktbuf_release(pkt);
return;
}
DEBUG("ipv6: packet is addressed to myself => loopback\n");
if (gnrc_netapi_dispatch_receive(GNRC_NETTYPE_IPV6,
GNRC_NETREG_DEMUX_CTX_ALL,
pkt) == 0) {
DEBUG("ipv6: unable to deliver looped back packet\n");
gnrc_pktbuf_release(pkt);
}
2015-03-03 22:20:21 +01:00
}
static void _send(gnrc_pktsnip_t *pkt, bool prep_hdr)
2015-03-03 22:20:21 +01:00
{
gnrc_netif_t *netif = NULL;
gnrc_pktsnip_t *tmp_pkt;
ipv6_hdr_t *ipv6_hdr;
uint8_t netif_hdr_flags = 0U;
2015-03-03 22:20:21 +01:00
/* get IPv6 snip and (if present) generic interface header */
if (pkt->type == GNRC_NETTYPE_NETIF) {
2015-03-03 22:20:21 +01:00
/* If there is already a netif header (routing protocols and
* neighbor discovery might add them to preset sending interface or
* higher layers wants to provide flags to the interface ) */
const gnrc_netif_hdr_t *netif_hdr = pkt->data;
netif = gnrc_netif_hdr_get_netif(pkt->data);
/* discard broadcast and multicast flags because those could be
* potentially wrong (dst is later checked to assure that multicast is
* set if dst is a multicast address) */
netif_hdr_flags = netif_hdr->flags &
~(GNRC_NETIF_HDR_FLAGS_BROADCAST |
GNRC_NETIF_HDR_FLAGS_MULTICAST);
tmp_pkt = gnrc_pktbuf_start_write(pkt);
if (tmp_pkt == NULL) {
2015-07-26 16:30:27 +02:00
DEBUG("ipv6: unable to get write access to netif header, dropping packet\n");
gnrc_pktbuf_release(pkt);
2015-07-26 16:30:27 +02:00
return;
}
/* discard to avoid complex checks for correctness (will be re-added
* with correct addresses anyway as for the case were there is no
* netif header provided)
* Also re-establish temporary pointer used for write protection as
* actual pointer */
pkt = gnrc_pktbuf_remove_snip(tmp_pkt, tmp_pkt);
}
if (pkt->type != GNRC_NETTYPE_IPV6) {
DEBUG("ipv6: unexpected packet type\n");
gnrc_pktbuf_release_error(pkt, EINVAL);
return;
2015-03-03 22:20:21 +01:00
}
if (ipv6_addr_is_unspecified(&((ipv6_hdr_t *)pkt->data)->dst)) {
DEBUG("ipv6: destination address is unspecified address (::), "
"dropping packet \n");
gnrc_pktbuf_release_error(pkt, EINVAL);
return;
}
tmp_pkt = gnrc_pktbuf_start_write(pkt);
if (tmp_pkt == NULL) {
2015-07-26 16:30:27 +02:00
DEBUG("ipv6: unable to get write access to IPv6 header, dropping packet\n");
gnrc_pktbuf_release(pkt);
2015-07-26 16:30:27 +02:00
return;
}
pkt = tmp_pkt;
2015-03-03 22:20:21 +01:00
ipv6_hdr = pkt->data;
2015-03-03 22:20:21 +01:00
if (ipv6_addr_is_multicast(&ipv6_hdr->dst)) {
_send_multicast(pkt, prep_hdr, netif, netif_hdr_flags);
2015-03-03 22:20:21 +01:00
}
else {
gnrc_netif_t *tmp_netif = gnrc_netif_get_by_ipv6_addr(&ipv6_hdr->dst);
2015-06-09 21:38:02 +02:00
if (ipv6_addr_is_loopback(&ipv6_hdr->dst) || /* dst is loopback address */
/* or dst registered to a local interface */
(tmp_netif != NULL)) {
_send_to_self(pkt, prep_hdr, tmp_netif);
}
else {
_send_unicast(pkt, prep_hdr, netif, ipv6_hdr, netif_hdr_flags);
}
2015-03-03 22:20:21 +01:00
}
}
/* functions for receiving */
static inline bool _pkt_not_for_me(gnrc_netif_t **netif, ipv6_hdr_t *hdr)
2015-03-03 22:20:21 +01:00
{
2015-08-10 00:26:36 +02:00
if (ipv6_addr_is_loopback(&hdr->dst)) {
2015-06-09 21:38:02 +02:00
return false;
}
else if ((!ipv6_addr_is_link_local(&hdr->dst)) ||
(*netif == NULL)) {
*netif = gnrc_netif_get_by_ipv6_addr(&hdr->dst);
return (*netif == NULL);
2015-03-03 22:20:21 +01:00
}
else {
2017-11-16 18:06:46 +01:00
return (gnrc_netif_get_by_ipv6_addr(&hdr->dst) == NULL);
2015-03-03 22:20:21 +01:00
}
}
static void _receive(gnrc_pktsnip_t *pkt)
2015-03-03 22:20:21 +01:00
{
gnrc_netif_t *netif = NULL;
2018-10-23 21:53:30 +02:00
gnrc_pktsnip_t *ipv6, *netif_hdr;
ipv6_hdr_t *hdr;
uint8_t first_nh;
2015-03-03 22:20:21 +01:00
2015-07-22 14:38:28 +02:00
assert(pkt != NULL);
netif_hdr = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_NETIF);
if (netif_hdr != NULL) {
netif = gnrc_netif_get_by_pid(((gnrc_netif_hdr_t *)netif_hdr->data)->if_pid);
#ifdef MODULE_NETSTATS_IPV6
assert(netif != NULL);
netstats_t *stats = &netif->ipv6.stats;
stats->rx_count++;
stats->rx_bytes += (gnrc_pkt_len(pkt) - netif_hdr->size);
#endif
}
2015-03-03 22:20:21 +01:00
2018-10-23 21:53:30 +02:00
if ((pkt->data == NULL) || (pkt->size < sizeof(ipv6_hdr_t)) ||
!ipv6_hdr_is(pkt->data)) {
DEBUG("ipv6: Received packet was not IPv6, dropping packet\n");
gnrc_pktbuf_release(pkt);
return;
2015-03-03 22:20:21 +01:00
}
#ifdef MODULE_GNRC_IPV6_WHITELIST
2018-10-23 21:53:30 +02:00
else if (!gnrc_ipv6_whitelisted(&((ipv6_hdr_t *)(pkt->data))->src)) {
DEBUG("ipv6: Source address not whitelisted, dropping packet\n");
gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_PROHIB, pkt);
gnrc_pktbuf_release(pkt);
return;
}
#endif
#ifdef MODULE_GNRC_IPV6_BLACKLIST
2018-10-23 21:53:30 +02:00
else if (gnrc_ipv6_blacklisted(&((ipv6_hdr_t *)(pkt->data))->src)) {
DEBUG("ipv6: Source address blacklisted, dropping packet\n");
gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_PROHIB, pkt);
gnrc_pktbuf_release(pkt);
return;
}
#endif
2018-10-23 21:53:30 +02:00
/* seize ipv6 as a temporary variable */
ipv6 = gnrc_pktbuf_start_write(pkt);
if (ipv6 == NULL) {
DEBUG("ipv6: unable to get write access to packet, drop it\n");
gnrc_pktbuf_release(pkt);
return;
}
pkt = ipv6; /* reset pkt from temporary variable */
ipv6 = gnrc_pktbuf_mark(pkt, sizeof(ipv6_hdr_t), GNRC_NETTYPE_IPV6);
2015-03-03 22:20:21 +01:00
2018-10-23 21:53:30 +02:00
pkt->type = GNRC_NETTYPE_UNDEF; /* snip is no longer IPv6 */
if (ipv6 == NULL) {
DEBUG("ipv6: error marking IPv6 header, dropping packet\n");
gnrc_pktbuf_release(pkt);
return;
}
2015-03-03 22:20:21 +01:00
/* extract header */
hdr = (ipv6_hdr_t *)ipv6->data;
2015-03-03 22:20:21 +01:00
2018-12-06 15:09:44 +01:00
if (hdr->hl == 0) {
/* This is an illegal value in any case, not just in case of a
* forwarding step, so *do not* check it together with ((--hdr->hl) > 0)
* in forwarding code below */
DEBUG("ipv6: packet was received with hop-limit 0\n");
gnrc_icmpv6_error_time_exc_send(ICMPV6_ERROR_TIME_EXC_HL, pkt);
gnrc_pktbuf_release_error(pkt, ETIMEDOUT);
return;
}
uint16_t ipv6_len = byteorder_ntohs(hdr->len);
first_nh = hdr->nh;
if ((ipv6_len == 0) && (first_nh != PROTNUM_IPV6_NONXT)) {
/* this doesn't even make sense */
DEBUG("ipv6: payload length 0, but next header not NONXT\n");
gnrc_pktbuf_release(pkt);
return;
}
/* if available, remove any padding that was added by lower layers
* to fulfill their minimum size requirements (e.g. ethernet) */
else if ((ipv6 != pkt) && (ipv6_len < pkt->size)) {
gnrc_pktbuf_realloc_data(pkt, byteorder_ntohs(hdr->len));
}
else if (ipv6_len > (gnrc_pkt_len_upto(pkt, GNRC_NETTYPE_IPV6) - sizeof(ipv6_hdr_t))) {
2016-03-11 04:44:14 +01:00
DEBUG("ipv6: invalid payload length: %d, actual: %d, dropping packet\n",
(int) byteorder_ntohs(hdr->len),
(int) (gnrc_pkt_len_upto(pkt, GNRC_NETTYPE_IPV6) - sizeof(ipv6_hdr_t)));
gnrc_icmpv6_error_param_prob_send(ICMPV6_ERROR_PARAM_PROB_HDR_FIELD,
&(hdr->len), pkt);
gnrc_pktbuf_release_error(pkt, EINVAL);
2016-03-11 04:44:14 +01:00
return;
}
2015-03-03 22:20:21 +01:00
DEBUG("ipv6: Received (src = %s, ",
2015-08-10 00:26:36 +02:00
ipv6_addr_to_str(addr_str, &(hdr->src), sizeof(addr_str)));
DEBUG("dst = %s, next header = %u, length = %" PRIu16 ")\n",
2015-08-10 00:26:36 +02:00
ipv6_addr_to_str(addr_str, &(hdr->dst), sizeof(addr_str)),
first_nh, byteorder_ntohs(hdr->len));
2015-03-03 22:20:21 +01:00
gnrc_ipv6: remove obsolete and harmful reception code When reworking the reception of IPv6 packets I reset a previously set `ipv6` snip as follows when the IPv6 extension handler returns a packet (see first hunk of this commit): ```C ipv6 = pkt->next->next ``` With `gnrc_ipv6_ext` this makes *somewhat* sense, `pkt->next` was previously equal to `ipv6` and after the function call `pkt->next` is the marked extension header, while `pkt->next->next` is the IPv6 header. However, since `ipv6` is already write-protected i.e. `ipv6->users == 1` (see ll. 665-675), any additional call of `gnrc_pktbuf_start_write()` [won't][start-write-doc] duplicate the packet. In fact, the only `gnrc_pktbuf_start_write()` in `gnrc_ipv6_ext` is used to send the *result* to the subscribers of that extension header type, leaving the original packet unchanged for the caller. As such `ipv6` remains the pointer to the IPv6 header whether we set it in the line above or not. So we actually don't need that line. However, the extension header handling also returns a packet when `gnrc_ipv6_ext` is not compiled in. In that case it is just a dummy define that returns the packet you give provide it which means that this still holds true: `pkt->next == ipv6`. So setting `ipv6` in this case is actually harmful, as `ipv6` now points to the NETIF header [following the IPv6 header][pkt-structure] in the packet and this causes the `user` counter of that NETIF header `hdr` to be decremented if `hdr->users > 1` in the write-protection I removed in hunk 2 of this commit: ```C /* pkt might not be writable yet, if header was given above */ ipv6 = gnrc_pktbuf_start_write(ipv6); if (ipv6 == NULL) { DEBUG("ipv6: unable to get write access to packet: dropping it\n"); gnrc_pktbuf_release(pkt); return; } ``` But as we already established, `ipv6->users` is already 1, so we don't actually need the write protection here either. Since the packet stays unchanged after the `ipv6` snip, we also don't need to re-search for `netif_hdr` after the other two lines are removed. [start-write-doc]: https://doc.riot-os.org/group__net__gnrc__pktbuf.html#ga640418467294ae3d408c109ab27bd617 [pkt-structure]: https://doc.riot-os.org/group__net__gnrc__pkt.html#ga278e783e56a5ee6f1bd7b81077ed82a7
2019-06-22 01:26:37 +02:00
if ((pkt = gnrc_ipv6_ext_process_hopopt(pkt, &first_nh)) == NULL) {
DEBUG("ipv6: packet's extension header was errorneous or packet was "
"consumed due to it\n");
return;
}
if (_pkt_not_for_me(&netif, hdr)) { /* if packet is not for me */
2015-03-03 22:20:21 +01:00
DEBUG("ipv6: packet destination not this host\n");
#ifdef MODULE_GNRC_IPV6_ROUTER /* only routers redirect */
2015-03-03 22:20:21 +01:00
/* redirect to next hop */
DEBUG("ipv6: decrement hop limit to %u\n", (uint8_t) (hdr->hl - 1));
2015-03-03 22:20:21 +01:00
/* RFC 4291, section 2.5.6 states: "Routers must not forward any
* packets with Link-Local source or destination addresses to other
* links."
*/
if ((ipv6_addr_is_link_local(&(hdr->src))) || (ipv6_addr_is_link_local(&(hdr->dst)))) {
2016-04-09 22:11:21 +02:00
DEBUG("ipv6: do not forward packets with link-local source or"
" destination address\n");
#ifdef MODULE_GNRC_ICMPV6_ERROR
if (ipv6_addr_is_link_local(&(hdr->src)) &&
!ipv6_addr_is_link_local(&(hdr->dst))) {
gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_SCOPE, pkt);
}
else if (!ipv6_addr_is_multicast(&(hdr->dst))) {
gnrc_icmpv6_error_dst_unr_send(ICMPV6_ERROR_DST_UNR_ADDR, pkt);
}
#endif
gnrc_pktbuf_release(pkt);
return;
}
2015-03-03 22:20:21 +01:00
/* TODO: check if receiving interface is router */
else if (--(hdr->hl) > 0) { /* drop packets that *reach* Hop Limit 0 */
2015-03-03 22:20:21 +01:00
DEBUG("ipv6: forward packet to next hop\n");
/* remove L2 headers around IPV6 */
if (netif_hdr != NULL) {
gnrc_pktbuf_remove_snip(pkt, netif_hdr);
}
pkt = gnrc_pktbuf_reverse_snips(pkt);
if (pkt != NULL) {
_send(pkt, false);
}
else {
DEBUG("ipv6: unable to reverse pkt from receive order to send "
"order; dropping it\n");
}
return;
2015-03-03 22:20:21 +01:00
}
else {
DEBUG("ipv6: hop limit reached 0: drop packet\n");
gnrc_icmpv6_error_time_exc_send(ICMPV6_ERROR_TIME_EXC_HL, pkt);
gnrc_pktbuf_release_error(pkt, ETIMEDOUT);
2015-03-03 22:20:21 +01:00
return;
}
#else /* MODULE_GNRC_IPV6_ROUTER */
2015-03-03 22:20:21 +01:00
DEBUG("ipv6: dropping packet\n");
/* non rounting hosts just drop the packet */
gnrc_pktbuf_release(pkt);
2015-03-03 22:20:21 +01:00
return;
#endif /* MODULE_GNRC_IPV6_ROUTER */
2015-03-03 22:20:21 +01:00
}
if ((pkt = gnrc_ipv6_ext_process_all(pkt, &first_nh)) == NULL) {
DEBUG("ipv6: packet was consumed in extension header handling\n");
return;
}
_demux(netif, pkt, first_nh);
}
2015-03-03 22:20:21 +01:00
/** @} */