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