2015-04-24 15:06:14 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2015 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2015-08-17 15:41:29 +02:00
|
|
|
* @ingroup net_gnrc_udp
|
2015-04-24 15:06:14 +02:00
|
|
|
* @{
|
|
|
|
*
|
|
|
|
* @file
|
|
|
|
* @brief UDP implementation
|
|
|
|
*
|
|
|
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
|
|
|
* @}
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include "byteorder.h"
|
|
|
|
#include "msg.h"
|
|
|
|
#include "thread.h"
|
|
|
|
#include "utlist.h"
|
2015-08-09 23:53:40 +02:00
|
|
|
#include "net/ipv6/hdr.h"
|
2015-08-17 15:41:29 +02:00
|
|
|
#include "net/gnrc/udp.h"
|
2015-08-10 02:41:08 +02:00
|
|
|
#include "net/gnrc.h"
|
2015-08-07 14:56:36 +02:00
|
|
|
#include "net/inet_csum.h"
|
2015-04-24 15:06:14 +02:00
|
|
|
|
|
|
|
|
|
|
|
#define ENABLE_DEBUG (0)
|
|
|
|
#include "debug.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Save the UDP's thread PID for later reference
|
|
|
|
*/
|
|
|
|
static kernel_pid_t _pid = KERNEL_PID_UNDEF;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Allocate memory for the UDP thread's stack
|
|
|
|
*/
|
2015-05-27 12:05:06 +02:00
|
|
|
#if ENABLE_DEBUG
|
2015-08-17 15:41:29 +02:00
|
|
|
static char _stack[GNRC_UDP_STACK_SIZE + THREAD_EXTRA_STACKSIZE_PRINTF];
|
2015-05-27 12:05:06 +02:00
|
|
|
#else
|
2015-08-17 15:41:29 +02:00
|
|
|
static char _stack[GNRC_UDP_STACK_SIZE];
|
2015-05-27 12:05:06 +02:00
|
|
|
#endif
|
2015-04-24 15:06:14 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Calculate the UDP checksum dependent on the network protocol
|
|
|
|
*
|
|
|
|
* @note If the checksum turns out to be 0x0000, the function returns 0xffff
|
|
|
|
* as specified in RFC768
|
|
|
|
*
|
|
|
|
* @param[in] pkt pointer to the packet in the packet buffer
|
|
|
|
* @param[in] pseudo_hdr pointer to the network layer header
|
|
|
|
* @param[in] payload pointer to the payload
|
|
|
|
*
|
|
|
|
* @return the checksum of the pkt in host byte order
|
|
|
|
* @return 0 on error
|
|
|
|
*/
|
2015-08-17 15:41:29 +02:00
|
|
|
static uint16_t _calc_csum(gnrc_pktsnip_t *hdr, gnrc_pktsnip_t *pseudo_hdr,
|
|
|
|
gnrc_pktsnip_t *payload)
|
2015-04-24 15:06:14 +02:00
|
|
|
{
|
|
|
|
uint16_t csum = 0;
|
|
|
|
uint16_t len = (uint16_t)hdr->size;
|
|
|
|
|
|
|
|
/* process the payload */
|
2015-10-30 18:54:38 +01:00
|
|
|
while (payload && payload != hdr && payload != pseudo_hdr) {
|
2015-11-28 22:11:07 +01:00
|
|
|
csum = inet_csum_slice(csum, (uint8_t *)(payload->data), payload->size, len);
|
2015-04-24 15:06:14 +02:00
|
|
|
len += (uint16_t)payload->size;
|
|
|
|
payload = payload->next;
|
|
|
|
}
|
|
|
|
/* process applicable UDP header bytes */
|
2015-08-10 03:16:53 +02:00
|
|
|
csum = inet_csum(csum, (uint8_t *)hdr->data, sizeof(udp_hdr_t));
|
2015-04-24 15:06:14 +02:00
|
|
|
|
|
|
|
switch (pseudo_hdr->type) {
|
2015-08-17 15:41:29 +02:00
|
|
|
#ifdef MODULE_GNRC_IPV6
|
|
|
|
case GNRC_NETTYPE_IPV6:
|
2015-08-09 23:53:40 +02:00
|
|
|
csum = ipv6_hdr_inet_csum(csum, pseudo_hdr->data, PROTNUM_UDP, len);
|
2015-04-24 15:06:14 +02:00
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
(void)len;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* return inverted results */
|
2016-02-22 12:30:31 +01:00
|
|
|
if (csum == 0xFFFF) {
|
|
|
|
/* https://tools.ietf.org/html/rfc2460#section-8.1
|
|
|
|
* bullet 4
|
|
|
|
* "if that computation yields a result of zero, it must be changed
|
|
|
|
* to hex FFFF for placement in the UDP header."
|
|
|
|
*/
|
|
|
|
return 0xFFFF;
|
|
|
|
} else {
|
|
|
|
return ~csum;
|
|
|
|
}
|
2015-04-24 15:06:14 +02:00
|
|
|
}
|
|
|
|
|
2015-08-17 15:41:29 +02:00
|
|
|
static void _receive(gnrc_pktsnip_t *pkt)
|
2015-04-24 15:06:14 +02:00
|
|
|
{
|
2015-08-17 15:41:29 +02:00
|
|
|
gnrc_pktsnip_t *udp, *ipv6;
|
2015-08-10 03:16:53 +02:00
|
|
|
udp_hdr_t *hdr;
|
2015-04-24 15:06:14 +02:00
|
|
|
uint32_t port;
|
|
|
|
|
|
|
|
/* mark UDP header */
|
2015-08-17 15:41:29 +02:00
|
|
|
udp = gnrc_pktbuf_start_write(pkt);
|
2015-04-24 15:06:14 +02:00
|
|
|
if (udp == NULL) {
|
|
|
|
DEBUG("udp: unable to get write access to packet\n");
|
2015-08-17 15:41:29 +02:00
|
|
|
gnrc_pktbuf_release(pkt);
|
2015-04-24 15:06:14 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
pkt = udp;
|
2015-10-30 18:54:38 +01:00
|
|
|
|
2016-02-11 00:48:06 +01:00
|
|
|
ipv6 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6);
|
2015-10-30 18:54:38 +01:00
|
|
|
|
|
|
|
assert(ipv6 != NULL);
|
|
|
|
|
2015-12-18 18:29:34 +01:00
|
|
|
if ((pkt->next != NULL) && (pkt->next->type == GNRC_NETTYPE_UDP) &&
|
|
|
|
(pkt->next->size == sizeof(udp_hdr_t))) {
|
2015-10-30 18:54:38 +01:00
|
|
|
/* UDP header was already marked. Take it. */
|
2015-12-18 18:29:34 +01:00
|
|
|
udp = pkt->next;
|
2015-10-30 18:54:38 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
udp = gnrc_pktbuf_mark(pkt, sizeof(udp_hdr_t), GNRC_NETTYPE_UDP);
|
|
|
|
if (udp == NULL) {
|
|
|
|
DEBUG("udp: error marking UDP header, dropping packet\n");
|
|
|
|
gnrc_pktbuf_release(pkt);
|
|
|
|
return;
|
|
|
|
}
|
2015-04-24 15:06:14 +02:00
|
|
|
}
|
2015-06-22 16:37:03 +02:00
|
|
|
/* mark payload as Type: UNDEF */
|
2015-08-17 15:41:29 +02:00
|
|
|
pkt->type = GNRC_NETTYPE_UNDEF;
|
2015-06-22 16:37:03 +02:00
|
|
|
/* get explicit pointer to UDP header */
|
2015-08-10 03:16:53 +02:00
|
|
|
hdr = (udp_hdr_t *)udp->data;
|
2015-04-24 15:06:14 +02:00
|
|
|
|
|
|
|
/* validate checksum */
|
2016-02-22 12:36:14 +01:00
|
|
|
if (byteorder_ntohs(hdr->checksum) == 0) {
|
|
|
|
/* RFC 2460 Section 8.1
|
|
|
|
* "IPv6 receivers must discard UDP packets containing a zero checksum,
|
|
|
|
* and should log the error."
|
|
|
|
*/
|
|
|
|
DEBUG("udp: received packet with zero checksum, dropping it\n");
|
|
|
|
gnrc_pktbuf_release(pkt);
|
|
|
|
return;
|
|
|
|
}
|
2016-02-22 12:30:31 +01:00
|
|
|
if (_calc_csum(udp, ipv6, pkt) != 0xFFFF) {
|
2015-04-24 15:06:14 +02:00
|
|
|
DEBUG("udp: received packet with invalid checksum, dropping it\n");
|
2015-08-17 15:41:29 +02:00
|
|
|
gnrc_pktbuf_release(pkt);
|
2015-04-24 15:06:14 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get port (netreg demux context) */
|
|
|
|
port = (uint32_t)byteorder_ntohs(hdr->dst_port);
|
|
|
|
|
|
|
|
/* send payload to receivers */
|
2015-08-17 15:41:29 +02:00
|
|
|
if (!gnrc_netapi_dispatch_receive(GNRC_NETTYPE_UDP, port, pkt)) {
|
2015-04-24 15:06:14 +02:00
|
|
|
DEBUG("udp: unable to forward packet as no one is interested in it\n");
|
2015-08-17 15:41:29 +02:00
|
|
|
gnrc_pktbuf_release(pkt);
|
2015-04-24 15:06:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-17 15:41:29 +02:00
|
|
|
static void _send(gnrc_pktsnip_t *pkt)
|
2015-04-24 15:06:14 +02:00
|
|
|
{
|
2015-08-10 03:16:53 +02:00
|
|
|
udp_hdr_t *hdr;
|
2015-08-25 18:12:05 +02:00
|
|
|
gnrc_pktsnip_t *udp_snip, *tmp;
|
2015-04-24 15:06:14 +02:00
|
|
|
|
2015-08-25 18:12:05 +02:00
|
|
|
/* write protect first header */
|
|
|
|
tmp = gnrc_pktbuf_start_write(pkt);
|
|
|
|
if (tmp == NULL) {
|
|
|
|
DEBUG("udp: cannot send packet: unable to allocate packet\n");
|
|
|
|
gnrc_pktbuf_release(pkt);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pkt = tmp;
|
|
|
|
udp_snip = tmp->next;
|
|
|
|
|
|
|
|
/* get and write protect until udp snip */
|
|
|
|
while ((udp_snip != NULL) && (udp_snip->type != GNRC_NETTYPE_UDP)) {
|
|
|
|
udp_snip = gnrc_pktbuf_start_write(udp_snip);
|
|
|
|
if (udp_snip == NULL) {
|
|
|
|
DEBUG("udp: cannot send packet: unable to allocate packet\n");
|
|
|
|
gnrc_pktbuf_release(pkt);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
tmp->next = udp_snip;
|
|
|
|
tmp = udp_snip;
|
|
|
|
udp_snip = udp_snip->next;
|
|
|
|
}
|
2015-07-22 14:38:28 +02:00
|
|
|
|
|
|
|
assert(udp_snip != NULL);
|
|
|
|
|
2015-08-25 18:12:05 +02:00
|
|
|
/* write protect UDP snip */
|
2015-08-17 15:41:29 +02:00
|
|
|
udp_snip = gnrc_pktbuf_start_write(udp_snip);
|
2015-04-24 15:06:14 +02:00
|
|
|
if (udp_snip == NULL) {
|
2015-04-16 05:09:35 +02:00
|
|
|
DEBUG("udp: cannot send packet: unable to allocate packet\n");
|
2015-08-17 15:41:29 +02:00
|
|
|
gnrc_pktbuf_release(pkt);
|
2015-04-24 15:06:14 +02:00
|
|
|
return;
|
|
|
|
}
|
2015-08-10 03:16:53 +02:00
|
|
|
hdr = (udp_hdr_t *)udp_snip->data;
|
2015-04-24 15:06:14 +02:00
|
|
|
/* fill in size field */
|
2015-08-17 15:41:29 +02:00
|
|
|
hdr->length = byteorder_htons(gnrc_pkt_len(udp_snip));
|
2015-04-24 15:06:14 +02:00
|
|
|
|
|
|
|
/* and forward packet to the network layer */
|
2015-08-17 15:41:29 +02:00
|
|
|
if (!gnrc_netapi_dispatch_send(pkt->type, GNRC_NETREG_DEMUX_CTX_ALL, pkt)) {
|
2015-04-16 05:09:35 +02:00
|
|
|
DEBUG("udp: cannot send packet: network layer not found\n");
|
2015-08-17 15:41:29 +02:00
|
|
|
gnrc_pktbuf_release(pkt);
|
2015-04-24 15:06:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *_event_loop(void *arg)
|
|
|
|
{
|
|
|
|
(void)arg;
|
|
|
|
msg_t msg, reply;
|
2015-08-17 15:41:29 +02:00
|
|
|
msg_t msg_queue[GNRC_UDP_MSG_QUEUE_SIZE];
|
|
|
|
gnrc_netreg_entry_t netreg;
|
2015-04-24 15:06:14 +02:00
|
|
|
|
|
|
|
/* preset reply message */
|
2015-08-17 15:41:29 +02:00
|
|
|
reply.type = GNRC_NETAPI_MSG_TYPE_ACK;
|
2015-04-24 15:06:14 +02:00
|
|
|
reply.content.value = (uint32_t)-ENOTSUP;
|
|
|
|
/* initialize message queue */
|
2015-08-17 15:41:29 +02:00
|
|
|
msg_init_queue(msg_queue, GNRC_UDP_MSG_QUEUE_SIZE);
|
2015-04-24 15:06:14 +02:00
|
|
|
/* register UPD at netreg */
|
2015-08-17 15:41:29 +02:00
|
|
|
netreg.demux_ctx = GNRC_NETREG_DEMUX_CTX_ALL;
|
2015-04-24 15:06:14 +02:00
|
|
|
netreg.pid = thread_getpid();
|
2015-08-17 15:41:29 +02:00
|
|
|
gnrc_netreg_register(GNRC_NETTYPE_UDP, &netreg);
|
2015-04-24 15:06:14 +02:00
|
|
|
|
|
|
|
/* dispatch NETAPI messages */
|
|
|
|
while (1) {
|
|
|
|
msg_receive(&msg);
|
|
|
|
switch (msg.type) {
|
2015-08-17 15:41:29 +02:00
|
|
|
case GNRC_NETAPI_MSG_TYPE_RCV:
|
|
|
|
DEBUG("udp: GNRC_NETAPI_MSG_TYPE_RCV\n");
|
2016-06-02 20:40:15 +02:00
|
|
|
_receive(msg.content.ptr);
|
2015-04-24 15:06:14 +02:00
|
|
|
break;
|
2015-08-17 15:41:29 +02:00
|
|
|
case GNRC_NETAPI_MSG_TYPE_SND:
|
|
|
|
DEBUG("udp: GNRC_NETAPI_MSG_TYPE_SND\n");
|
2016-06-02 20:40:15 +02:00
|
|
|
_send(msg.content.ptr);
|
2015-04-24 15:06:14 +02:00
|
|
|
break;
|
2015-08-17 15:41:29 +02:00
|
|
|
case GNRC_NETAPI_MSG_TYPE_SET:
|
|
|
|
case GNRC_NETAPI_MSG_TYPE_GET:
|
2015-04-24 15:06:14 +02:00
|
|
|
msg_reply(&msg, &reply);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DEBUG("udp: received unidentified message\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* never reached */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-08-17 15:41:29 +02:00
|
|
|
int gnrc_udp_calc_csum(gnrc_pktsnip_t *hdr, gnrc_pktsnip_t *pseudo_hdr)
|
2015-04-24 15:06:14 +02:00
|
|
|
{
|
|
|
|
uint16_t csum;
|
|
|
|
|
2015-07-02 00:32:18 +02:00
|
|
|
if ((hdr == NULL) || (pseudo_hdr == NULL)) {
|
2015-04-24 15:06:14 +02:00
|
|
|
return -EFAULT;
|
|
|
|
}
|
2015-08-17 15:41:29 +02:00
|
|
|
if (hdr->type != GNRC_NETTYPE_UDP) {
|
2015-04-24 15:06:14 +02:00
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
|
|
|
csum = _calc_csum(hdr, pseudo_hdr, hdr->next);
|
|
|
|
if (csum == 0) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
2015-08-10 03:16:53 +02:00
|
|
|
((udp_hdr_t *)hdr->data)->checksum = byteorder_htons(csum);
|
2015-04-24 15:06:14 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-20 23:07:39 +01:00
|
|
|
gnrc_pktsnip_t *gnrc_udp_hdr_build(gnrc_pktsnip_t *payload, uint16_t src,
|
|
|
|
uint16_t dst)
|
2015-05-27 12:05:46 +02:00
|
|
|
{
|
2015-08-17 15:41:29 +02:00
|
|
|
gnrc_pktsnip_t *res;
|
2015-08-10 03:16:53 +02:00
|
|
|
udp_hdr_t *hdr;
|
2015-04-24 15:06:14 +02:00
|
|
|
|
|
|
|
/* allocate header */
|
2015-08-17 15:41:29 +02:00
|
|
|
res = gnrc_pktbuf_add(payload, NULL, sizeof(udp_hdr_t), GNRC_NETTYPE_UDP);
|
2015-04-24 15:06:14 +02:00
|
|
|
if (res == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* initialize header */
|
2015-08-10 03:16:53 +02:00
|
|
|
hdr = (udp_hdr_t *)res->data;
|
2016-03-20 23:07:39 +01:00
|
|
|
hdr->src_port = byteorder_htons(src);
|
|
|
|
hdr->dst_port = byteorder_htons(dst);
|
2015-04-24 15:06:14 +02:00
|
|
|
hdr->checksum = byteorder_htons(0);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2015-08-17 15:41:29 +02:00
|
|
|
int gnrc_udp_init(void)
|
2015-04-24 15:06:14 +02:00
|
|
|
{
|
|
|
|
/* check if thread is already running */
|
|
|
|
if (_pid == KERNEL_PID_UNDEF) {
|
|
|
|
/* start UDP thread */
|
2015-08-17 15:41:29 +02:00
|
|
|
_pid = thread_create(_stack, sizeof(_stack), GNRC_UDP_PRIO,
|
2015-12-02 12:00:19 +01:00
|
|
|
THREAD_CREATE_STACKTEST, _event_loop, NULL, "udp");
|
2015-04-24 15:06:14 +02:00
|
|
|
}
|
|
|
|
return _pid;
|
|
|
|
}
|