1 /* coap_io_riot.c -- Default network I/O functions for libcoap on RIOT
2  *
3  * Copyright (C) 2019 Olaf Bergmann <bergmann@tzi.org>
4  *
5  * SPDX-License-Identifier: BSD-2-Clause
6  *
7  * This file is part of the CoAP library libcoap. Please see
8  * README for terms of use.
9  */
10 
11 #include "coap3/coap_internal.h"
12 
13 #ifdef HAVE_STDIO_H
14 #  include <stdio.h>
15 #endif
16 
17 #ifdef HAVE_SYS_SOCKET_H
18 # include <sys/socket.h>
19 # define OPTVAL_T(t)         (t)
20 # define OPTVAL_GT(t)        (t)
21 #endif
22 #ifdef HAVE_SYS_IOCTL_H
23  #include <sys/ioctl.h>
24 #endif
25 #ifdef HAVE_NETINET_IN_H
26 # include <netinet/in.h>
27 #endif
28 #ifdef HAVE_SYS_UIO_H
29 # include <sys/uio.h>
30 #endif
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34 #include <errno.h>
35 
36 #include "net/gnrc.h"
37 #include "net/gnrc/ipv6.h"
38 #include "net/gnrc/netreg.h"
39 #include "net/udp.h"
40 
41 #include "coap_riot.h"
42 
43 ssize_t
coap_network_send(coap_socket_t * sock,const coap_session_t * session,const uint8_t * data,size_t datalen)44 coap_network_send(coap_socket_t *sock,
45                   const coap_session_t *session,
46                   const uint8_t *data,
47                   size_t datalen) {
48   ssize_t bytes_written = 0;
49 
50   if (!coap_debug_send_packet()) {
51     bytes_written = (ssize_t)datalen;
52   } else if (sock->flags & COAP_SOCKET_CONNECTED) {
53     bytes_written = send(sock->fd, data, datalen, 0);
54   } else {
55     bytes_written = sendto(sock->fd, data, datalen, 0,
56                            &session->addr_info.remote.addr.sa,
57                            session->addr_info.remote.size);
58   }
59 
60   if (bytes_written < 0)
61     coap_log(LOG_CRIT, "coap_network_send: %s\n", coap_socket_strerror());
62 
63   return bytes_written;
64 }
65 
66 static udp_hdr_t *
get_udp_header(gnrc_pktsnip_t * pkt)67 get_udp_header(gnrc_pktsnip_t *pkt) {
68   gnrc_pktsnip_t *udp = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_UDP);
69   return udp ? (udp_hdr_t *)udp->data : NULL;
70 }
71 
72 ssize_t
coap_network_read(coap_socket_t * sock,coap_packet_t * packet)73 coap_network_read(coap_socket_t *sock, coap_packet_t *packet) {
74   size_t len;
75   ipv6_hdr_t *ipv6_hdr;
76   /* The GNRC API currently only supports UDP. */
77   gnrc_pktsnip_t *udp;
78   udp_hdr_t *udp_hdr;
79   const gnrc_nettype_t type = GNRC_NETTYPE_UDP;
80 
81   assert(sock);
82   assert(packet);
83 
84   if ((sock->flags & COAP_SOCKET_CAN_READ) == 0) {
85     coap_log(LOG_DEBUG, "coap_network_read: COAP_SOCKET_CAN_READ not set\n");
86     return -1;
87   } else {
88     /* clear has-data flag */
89     sock->flags &= ~COAP_SOCKET_CAN_READ;
90   }
91 
92   /* Search for the transport header in the packet received from the
93    * network interface driver. */
94   udp = gnrc_pktsnip_search_type(sock->pkt, type);
95   ipv6_hdr = gnrc_ipv6_get_header(sock->pkt);
96 
97   if (!ipv6_hdr || !udp || !(udp_hdr = (udp_hdr_t *)udp->data)) {
98     coap_log(LOG_DEBUG, "no UDP header found in packet\n");
99     return -EFAULT;
100   }
101   udp_hdr_print(udp_hdr);
102 
103   len = (size_t)gnrc_pkt_len_upto(sock->pkt, type) - sizeof(udp_hdr_t);
104   coap_log(LOG_DEBUG, "coap_network_read: recvfrom got %zd bytes\n", len);
105   if (len > COAP_RXBUFFER_SIZE) {
106     coap_log(LOG_WARNING, "packet exceeds buffer size, truncated\n");
107     len = COAP_RXBUFFER_SIZE;
108   }
109   packet->ifindex = sock->fd;
110 
111   assert(sizeof(struct in6_addr) == sizeof(ipv6_addr_t));
112   packet->addr_info.remote.size = sizeof(struct sockaddr_in6);
113   memset(&packet->addr_info.remote.addr, 0,
114          sizeof(packet->addr_info.remote.addr));
115   packet->addr_info.remote.addr.sin6.sin6_family = AF_INET6;
116   memcpy(&packet->addr_info.remote.addr.sin6.sin6_addr,
117          &ipv6_hdr->src, sizeof(ipv6_addr_t));
118   memcpy(&packet->addr_info.remote.addr.sin6.sin6_port,
119          &udp_hdr->src_port, sizeof(udp_hdr->src_port));
120 
121   packet->addr_info.local.size = sizeof(struct sockaddr_in6);
122   memset(&packet->addr_info.local.addr, 0, sizeof(packet->addr_info.local.addr));
123   packet->addr_info.local.addr.sin6.sin6_family = AF_INET6;
124   memcpy(&packet->addr_info.local.addr.sin6.sin6_addr,
125          &ipv6_hdr->dst, sizeof(ipv6_addr_t));
126   memcpy(&packet->addr_info.local
127          .addr.sin6.sin6_port, &udp_hdr->dst_port, sizeof(udp_hdr->src_port));
128 
129   packet->ifindex = sock->fd;
130   packet->length = (len > 0) ? len : 0;
131   memcpy(packet->payload, (uint8_t*)udp_hdr + sizeof(udp_hdr_t), len);
132   if (LOG_DEBUG <= coap_get_log_level()) {
133     unsigned char addr_str[INET6_ADDRSTRLEN + 8];
134 
135     if (coap_print_addr(&packet->addr_info.remote, addr_str, INET6_ADDRSTRLEN + 8)) {
136       coap_log(LOG_DEBUG, "received %zd bytes from %s\n", len, addr_str);
137     }
138   }
139 
140   return len;
141 }
142 
143 static msg_t _msg_q[LIBCOAP_MSG_QUEUE_SIZE];
144 
145 void
coap_riot_startup(void)146 coap_riot_startup(void) {
147   msg_init_queue(_msg_q, LIBCOAP_MSG_QUEUE_SIZE);
148 }
149 
150 /**
151  * Returns the port of @p addr in network byte order or 0 on error.
152  */
153 static uint16_t
get_port(const coap_address_t * addr)154 get_port(const coap_address_t *addr) {
155   if (addr) {
156     switch (addr->addr.sa.sa_family) {
157     case AF_INET: return addr->addr.sin.sin_port;
158     case AF_INET6: return addr->addr.sin6.sin6_port;
159     default:
160       ;
161     }
162   }
163   return 0;
164 }
165 
166 static coap_socket_t *
find_socket(coap_fd_t fd,coap_socket_t * sockets[],unsigned int num_sockets)167 find_socket(coap_fd_t fd, coap_socket_t *sockets[], unsigned int num_sockets) {
168   for (unsigned int i = 0; i < num_sockets; i++) {
169     if (fd == sockets[i]->fd)
170       return sockets[i];
171   }
172   return NULL;
173 }
174 
175 static bool
address_equals(const coap_address_t * a,const ipv6_addr_t * b)176 address_equals(const coap_address_t *a, const ipv6_addr_t *b) {
177   assert(a);
178   assert(b);
179   return IN6_IS_ADDR_UNSPECIFIED(&a->addr.sin6.sin6_addr) ||
180     (memcmp(&a->addr.sin6.sin6_addr, b->u8, sizeof(struct in6_addr)) == 0);
181 }
182 
183 int
coap_io_process(coap_context_t * ctx,uint32_t timeout_ms)184 coap_io_process(coap_context_t *ctx, uint32_t timeout_ms) {
185   coap_tick_t before, now;
186   coap_socket_t *sockets[LIBCOAP_MAX_SOCKETS];
187   unsigned int num_sockets = 0, timeout;
188   gnrc_netreg_entry_t coap_reg =
189     GNRC_NETREG_ENTRY_INIT_PID(GNRC_NETREG_DEMUX_CTX_ALL, thread_getpid());
190   msg_t msg;
191   bool found_port = false;
192 
193   coap_ticks(&before);
194 
195   timeout =
196     coap_io_prepare_io(ctx, sockets, ARRAY_SIZE(sockets), &num_sockets, before);
197   if (timeout == 0 || timeout_ms < timeout)
198     timeout = timeout_ms;
199 
200   if (num_sockets > 0) {
201     gnrc_netreg_register(GNRC_NETTYPE_UDP, &coap_reg);
202   }
203 
204   if (timeout == 0 || timeout_ms < timeout)
205     timeout = timeout_ms;
206 
207   xtimer_msg_receive_timeout(&msg, timeout_ms * US_PER_SEC);
208   switch (msg.type) {
209   case GNRC_NETAPI_MSG_TYPE_RCV: {
210     coap_log(LOG_DEBUG, "coap_run_once: GNRC_NETAPI_MSG_TYPE_RCV\n");
211 
212     coap_session_t *s, *rtmp;
213     udp_hdr_t *udp_hdr = get_udp_header((gnrc_pktsnip_t *)msg.content.ptr);
214     ipv6_hdr_t *ip6_hdr =
215       gnrc_ipv6_get_header((gnrc_pktsnip_t *)msg.content.ptr);
216     if (!udp_hdr || !ip6_hdr)
217       break;
218     coap_log(LOG_DEBUG, "coap_run_once: found UDP header\n");
219 
220     /* Traverse all sessions and set COAP_SOCKET_CAN_READ if the
221      * received packet's destination address matches. */
222     SESSIONS_ITER(ctx->sessions, s, rtmp) {
223       coap_log(LOG_DEBUG, "coap_run_once: check ctx->sessions %u == %u\n",
224                ntohs(get_port(&s->addr_info.local)),
225                ntohs(udp_hdr->dst_port.u16));
226       if ((get_port(&s->addr_info.local) == udp_hdr->dst_port.u16) &&
227           (address_equals(&s->addr_info.local, &ip6_hdr->dst))) {
228         coap_socket_t *sock = find_socket(s->sock.fd, sockets, num_sockets);
229 
230         if (sock && (sock->flags & (COAP_SOCKET_WANT_READ))) {
231           coap_log(LOG_DEBUG, "fd %d on port %u can read\n",
232                    sock->fd, ntohs(get_port(&s->addr_info.local)));
233           sock->flags |= COAP_SOCKET_CAN_READ;
234           sock->pkt = msg.content.ptr;
235           found_port = true;
236           break;                /* found session, finish loop */
237         }
238       }
239     }
240 
241     /* If no session was found for received packet, traverse all
242      * endpoints and set COAP_SOCKET_CAN_READ if the received packet's
243      * destination address matches the endpoint. */
244     if (!found_port) {
245       coap_endpoint_t *ep;
246       LL_FOREACH(ctx->endpoint, ep) {
247         if ((get_port(&ep->bind_addr) == udp_hdr->dst_port.u16) &&
248             (address_equals(&ep->bind_addr, &ip6_hdr->dst))) {
249           coap_socket_t *sock = find_socket(ep->sock.fd, sockets, num_sockets);
250 
251           if (sock && (sock->flags & (COAP_SOCKET_WANT_READ))) {
252               coap_log(LOG_DEBUG, "fd %d on port %u can read\n",
253                        sock->fd, ntohs(get_port(&ep->bind_addr)));
254               sock->flags |= COAP_SOCKET_CAN_READ;
255               sock->pkt = msg.content.ptr;
256               found_port = true;
257               break;            /* found session, finish loop */
258             }
259         }
260       }
261     }
262     break;
263   }
264   case GNRC_NETAPI_MSG_TYPE_SND:
265     break;
266   case GNRC_NETAPI_MSG_TYPE_SET:
267     /* fall through */
268   case GNRC_NETAPI_MSG_TYPE_GET:
269     break;
270   default:
271     break;
272   }
273 
274   coap_ticks(&now);
275   coap_io_do_io(ctx, now);
276 
277   /* cleanup */
278   gnrc_netreg_unregister(GNRC_NETTYPE_UDP, &coap_reg);
279 
280   return (int)(((now - before) * 1000) / COAP_TICKS_PER_SECOND);
281 }
282