1 /* coap_io_contiki.c -- Network I/O functions for libcoap on Contiki-NG
2 *
3 * Copyright (C) 2012,2014 Olaf Bergmann <bergmann@tzi.org>
4 * 2014 chrysn <chrysn@fsfe.org>
5 *
6 * SPDX-License-Identifier: BSD-2-Clause
7 *
8 * This file is part of the CoAP library libcoap. Please see
9 * README for terms of use.
10 */
11
12 /**
13 * @file coap_io_contiki.c
14 * @brief Contiki-NG-specific functions
15 */
16
17 #include "coap3/coap_internal.h"
18 #include "contiki-net.h"
19
20 PROCESS(libcoap_io_process, "libcoap I/O");
21 static int was_io_process_stopped;
22
23 void
coap_start_io_process(void)24 coap_start_io_process(void) {
25 was_io_process_stopped = 0;
26 process_start(&libcoap_io_process, NULL);
27 }
28
29 void
coap_stop_io_process(void)30 coap_stop_io_process(void) {
31 was_io_process_stopped = 1;
32 process_poll(&libcoap_io_process);
33 }
34
35 static void
on_prepare_timer_expired(void * ptr)36 on_prepare_timer_expired(void *ptr) {
37 coap_context_t *ctx;
38 coap_tick_t now;
39 coap_socket_t *sockets[1];
40 unsigned int max_sockets = sizeof(sockets)/sizeof(sockets[0]);
41 unsigned int num_sockets;
42 unsigned timeout;
43
44 ctx = (coap_context_t *)ptr;
45 coap_ticks(&now);
46 timeout = coap_io_prepare_io(ctx, sockets, max_sockets, &num_sockets, now);
47 if (!timeout) {
48 return;
49 }
50 ctimer_set(&ctx->prepare_timer,
51 CLOCK_SECOND * timeout / 1000,
52 on_prepare_timer_expired,
53 ctx);
54 }
55
PROCESS_THREAD(libcoap_io_process,ev,data)56 PROCESS_THREAD(libcoap_io_process, ev, data) {
57 coap_socket_t *coap_socket;
58
59 PROCESS_BEGIN();
60 while (!was_io_process_stopped) {
61 PROCESS_WAIT_EVENT();
62 if (was_io_process_stopped) {
63 break;
64 }
65 if (ev == tcpip_event) {
66 coap_socket = (struct coap_socket_t *)data;
67 if (!coap_socket) {
68 coap_log_crit("libcoap_io_process: data should never be NULL\n");
69 continue;
70 }
71 if (uip_newdata()) {
72 coap_socket->flags |= COAP_SOCKET_CAN_READ;
73 coap_io_process(coap_socket->context, 0);
74 ctimer_stop(&coap_socket->context->prepare_timer);
75 on_prepare_timer_expired(coap_socket->context);
76 }
77 }
78 }
79 PROCESS_END();
80 }
81
82 int
coap_socket_bind_udp(coap_socket_t * sock,const coap_address_t * listen_addr,coap_address_t * bound_addr)83 coap_socket_bind_udp(coap_socket_t *sock,
84 const coap_address_t *listen_addr,
85 coap_address_t *bound_addr) {
86 uip_ds6_addr_t *addr;
87
88 addr = uip_ds6_get_global(ADDR_PREFERRED);
89 if (!addr) {
90 coap_log_err("coap_socket_bind_udp: called before getting an IPv6 address\n");
91 return 0;
92 }
93 PROCESS_CONTEXT_BEGIN(&libcoap_io_process);
94 sock->udp_conn = udp_new(NULL, 0, sock);
95 PROCESS_CONTEXT_END();
96 if (!sock->udp_conn) {
97 coap_log_err("coap_socket_bind_udp: udp_new returned NULL\n");
98 return 0;
99 }
100 udp_bind(sock->udp_conn, listen_addr->port);
101 uip_ipaddr_copy(&bound_addr->addr, &addr->ipaddr);
102 bound_addr->port = sock->udp_conn->lport;
103 return 1;
104 }
105
106 int
coap_socket_connect_udp(coap_socket_t * sock,const coap_address_t * local_if,const coap_address_t * server,int default_port,coap_address_t * local_addr,coap_address_t * remote_addr)107 coap_socket_connect_udp(coap_socket_t *sock,
108 const coap_address_t *local_if,
109 const coap_address_t *server,
110 int default_port,
111 coap_address_t *local_addr,
112 coap_address_t *remote_addr) {
113 uip_ds6_addr_t *addr;
114
115 if (local_if) {
116 coap_log_warn("coap_socket_connect_udp: ignoring local_if parameter\n");
117 }
118 addr = uip_ds6_get_global(ADDR_PREFERRED);
119 if (!addr) {
120 coap_log_err("coap_socket_connect_udp: called before getting an IPv6 address\n");
121 return 0;
122 }
123 PROCESS_CONTEXT_BEGIN(&libcoap_io_process);
124 sock->udp_conn = udp_new(&server->addr, server->port ? server->port : default_port, sock);
125 PROCESS_CONTEXT_END();
126 if (!sock->udp_conn) {
127 coap_log_err("coap_socket_connect_udp: udp_new returned NULL\n");
128 return 0;
129 }
130 uip_ipaddr_copy(&local_addr->addr, &addr->ipaddr);
131 local_addr->port = sock->udp_conn->lport;
132 uip_ipaddr_copy(&remote_addr->addr, &server->addr);
133 remote_addr->port = sock->udp_conn->rport;
134 sock->flags |= COAP_SOCKET_CONNECTED;
135 return 1;
136 }
137
138 ssize_t
coap_socket_write(coap_socket_t * sock,const uint8_t * data,size_t data_len)139 coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len) {
140 return -1;
141 }
142
143 ssize_t
coap_socket_read(coap_socket_t * sock,uint8_t * data,size_t data_len)144 coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) {
145 return -1;
146 }
147
148 void
coap_socket_close(coap_socket_t * sock)149 coap_socket_close(coap_socket_t *sock) {
150 uip_udp_remove(sock->udp_conn);
151 sock->udp_conn = NULL;
152 sock->flags = COAP_SOCKET_EMPTY;
153 }
154
155 /*
156 * dgram
157 * return +ve Number of bytes written.
158 * -1 Error error in errno).
159 */
160 ssize_t
coap_socket_send(coap_socket_t * sock,const coap_session_t * session,const uint8_t * data,size_t datalen)161 coap_socket_send(coap_socket_t *sock, const coap_session_t *session, const uint8_t *data,
162 size_t datalen) {
163 ssize_t bytes_written = 0;
164
165 if (!coap_debug_send_packet()) {
166 bytes_written = (ssize_t)datalen;
167 } else {
168 uip_udp_packet_sendto(sock->udp_conn, data, datalen,
169 &session->addr_info.remote.addr, session->addr_info.remote.port);
170 bytes_written = datalen;
171 }
172
173 if (bytes_written < 0) {
174 coap_log_crit("coap_socket_send: %s\n", coap_socket_strerror());
175 }
176
177 return bytes_written;
178 }
179
180 /*
181 * dgram
182 * return +ve Number of bytes written.
183 * -1 Error error in errno).
184 * -2 ICMP error response
185 */
186 ssize_t
coap_socket_recv(coap_socket_t * sock,coap_packet_t * packet)187 coap_socket_recv(coap_socket_t *sock, coap_packet_t *packet) {
188 ssize_t len;
189
190 assert(sock);
191 assert(packet);
192
193 if (!(sock->flags & COAP_SOCKET_CAN_READ)) {
194 return -1;
195 }
196 /* clear has-data flag */
197 sock->flags &= ~COAP_SOCKET_CAN_READ;
198
199 uip_ipaddr_copy(&packet->addr_info.remote.addr, &UIP_IP_BUF->srcipaddr);
200 packet->addr_info.remote.port = UIP_UDP_BUF->srcport;
201 uip_ipaddr_copy(&packet->addr_info.local.addr, &UIP_IP_BUF->destipaddr);
202 packet->addr_info.local.port = UIP_UDP_BUF->destport;
203
204 len = uip_datalen();
205
206 if (len > COAP_RXBUFFER_SIZE) {
207 coap_log_warn("Received message does not fit within buffer\n");
208 return -1;
209 }
210 packet->length = len;
211 packet->payload = uip_appdata;
212
213 return len;
214 }
215
216 int
coap_io_process(coap_context_t * ctx,uint32_t timeout_ms)217 coap_io_process(coap_context_t *ctx, uint32_t timeout_ms) {
218 coap_tick_t before, now;
219
220 coap_ticks(&before);
221 coap_io_do_io(ctx, before);
222 coap_ticks(&now);
223 return (int)(((now - before) * 1000) / COAP_TICKS_PER_SECOND);
224 }
225