6#include "dawn/proto/wakaama/wakaama.hxx"
8#include "transport.hxx"
15#ifdef CONFIG_DAWN_PROTO_WAKAAMA_DTLS_PSK
18# include <tinydtls/dtls.h>
19# include <tinydtls/tinydtls.h>
27#include <netinet/in.h>
29#include <sys/select.h>
30#include <sys/socket.h>
34using namespace dawn::wakaama_internal;
36#if defined(CONFIG_ARCH_SIM) && defined(CONFIG_SIM_NETUSRSOCK)
37# define DAWN_WAKAAMA_POLL_RECV 1
38extern "C" void host_usrsock_loop(
void);
44bool parseServerUri(
const char *uri,
char *host,
size_t hostSize, uint16_t *port, uint8_t *scheme);
45bool sockaddrEqual(
const sockaddr_storage &lhs,
47 const sockaddr_storage &rhs,
49#ifdef CONFIG_DAWN_PROTO_WAKAAMA_DTLS_PSK
50int dtlsSendToPeer(dtls_context_t *ctx, session_t *session, uint8 *data,
size_t len);
51int dtlsReadFromPeer(dtls_context_t *ctx, session_t *session, uint8 *data,
size_t len);
52int dtlsGetPskInfo(dtls_context_t *ctx,
53 const session_t *session,
54 dtls_credentials_type_t type,
55 const unsigned char *
id,
57 unsigned char *result,
64 , connections(nullptr)
66#ifdef CONFIG_DAWN_PROTO_WAKAAMA_DTLS_PSK
67 , dtlsContext(nullptr)
83 connectionPool.clear();
94 const size_t capacity = owner.serverPoolCapacity();
96 connectionPool.reserve(capacity);
97 for (
size_t i = 0; i < capacity; i++)
105 resetConnection(conn);
106 connectionPool.push_back(conn);
116 sockfd = socket(AF_INET, SOCK_DGRAM, 0);
122 std::memset(&addr, 0,
sizeof(addr));
123 addr.sin_family = AF_INET;
124 addr.sin_addr.s_addr = htonl(INADDR_ANY);
125 addr.sin_port = htons(owner.localPort);
126 if (bind(sockfd,
reinterpret_cast<sockaddr *
>(&addr),
sizeof(addr)) < 0)
139#ifdef CONFIG_DAWN_PROTO_WAKAAMA_DTLS_PSK
140 for (
const CProtoWakaama::ServerConfig &server : owner.servers)
142 if (server.scheme == CProtoWakaama::WAKAAMA_SERVER_SCHEME_COAPS)
145 dtlsContext = dtls_new_context(
this);
146 if (dtlsContext ==
nullptr)
151 std::memset(&dtlsHandler, 0,
sizeof(dtlsHandler));
152 dtlsHandler.write = dtlsSendToPeer;
153 dtlsHandler.read = dtlsReadFromPeer;
154 dtlsHandler.get_psk_info = dtlsGetPskInfo;
155 dtls_set_handler(dtlsContext, &dtlsHandler);
160 if (dtlsContext !=
nullptr)
162 for (
const CProtoWakaama::ServerConfig &server : owner.servers)
164 if (server.scheme == CProtoWakaama::WAKAAMA_SERVER_SCHEME_COAPS &&
178#ifdef CONFIG_DAWN_PROTO_WAKAAMA_DTLS_PSK
179 if (dtlsContext !=
nullptr)
181 dtls_free_context(dtlsContext);
182 dtlsContext =
nullptr;
190 sockaddr_in serverAddr;
191 const CProtoWakaama::ServerConfig *server;
194 uint16_t serverPortValue;
202 for (conn = connections; conn !=
nullptr; conn = conn->next)
204 if (conn->securityInstanceId == securityInstanceId)
210 server = owner.findServer(securityInstanceId);
214 if (server !=
nullptr)
216 std::snprintf(host,
sizeof(host),
"%s", server->host.c_str());
217 serverPortValue = server->port;
218 scheme = server->scheme;
220 else if (secInst !=
nullptr &&
221 parseServerUri(secInst->uri, host,
sizeof(host), &serverPortValue, &scheme))
223 if (scheme == CProtoWakaama::WAKAAMA_SERVER_SCHEME_COAPS)
225 DAWNERR(
"Wakaama bootstrap-created coaps server is not preallocated\n");
234 std::memset(&serverAddr, 0,
sizeof(serverAddr));
235 serverAddr.sin_family = AF_INET;
236 serverAddr.sin_port = htons(serverPortValue);
237 if (inet_pton(AF_INET, host, &serverAddr.sin_addr) != 1)
239 DAWNERR(
"Wakaama server host must be a numeric IPv4 address: %s\n", host);
258 resetConnection(conn);
260 conn->securityInstanceId = securityInstanceId;
261 if (server !=
nullptr)
263 conn->pskIdentity =
reinterpret_cast<const uint8_t *
>(server->pskIdentity.data());
264 conn->pskIdentityLen = server->pskIdentity.size();
265 conn->pskKey = server->pskKey.data();
266 conn->pskKeyLen = server->pskKey.size();
268 else if (secInst !=
nullptr)
270 conn->pskIdentity = secInst->publicIdentity;
271 conn->pskIdentityLen = secInst->publicIdentityLen;
272 conn->pskKey = secInst->secretKey;
273 conn->pskKeyLen = secInst->secretKeyLen;
275 std::memcpy(&conn->addr, &serverAddr,
sizeof(serverAddr));
276 conn->addrLen =
sizeof(serverAddr);
277#ifdef CONFIG_DAWN_PROTO_WAKAAMA_DTLS_PSK
278 conn->dtls = scheme == CProtoWakaama::WAKAAMA_SERVER_SCHEME_COAPS;
281 if (dtlsContext ==
nullptr)
283 resetConnection(conn);
287 std::memset(&conn->dtlsSession, 0,
sizeof(conn->dtlsSession));
288 std::memcpy(&conn->dtlsSession.addr.st, &conn->addr, conn->addrLen);
289 conn->dtlsSession.size = conn->addrLen;
290 conn->dtlsContext = dtlsContext;
291 conn->lastSend = lwm2m_gettime();
292 if (dtls_connect(conn->dtlsContext, &conn->dtlsSession) != 0)
294 resetConnection(conn);
299 conn->next = connections;
313 while (*link !=
nullptr)
318#ifdef CONFIG_DAWN_PROTO_WAKAAMA_DTLS_PSK
319 if (conn->dtls && conn->dtlsContext !=
nullptr)
321 dtls_peer_t *peer = dtls_get_peer(conn->dtlsContext, &conn->dtlsSession);
324 dtls_reset_peer(conn->dtlsContext, peer);
328 resetConnection(conn);
332 link = &(*link)->next;
338 const sockaddr_storage *peer =
static_cast<const sockaddr_storage *
>(addr);
345 for (
Connection *conn = connections; conn !=
nullptr; conn = conn->next)
347 if (sockaddrEqual(conn->addr, conn->addrLen, *peer,
static_cast<socklen_t
>(addrLen)))
358 if (owner.runtime !=
nullptr && conn !=
nullptr)
366 while (connections !=
nullptr)
373 resetConnection(conn);
376 connections =
nullptr;
379bool Transport::receiveOne(uint8_t *buffer,
size_t capacity)
381 sockaddr_storage addr;
382 socklen_t addrLen =
sizeof(addr);
385 len = recvfrom(sockfd, buffer, capacity, 0,
reinterpret_cast<sockaddr *
>(&addr), &addrLen);
388 Connection *conn = connections;
390 while (conn !=
nullptr)
392 if (sockaddrEqual(conn->addr, conn->addrLen, addr, addrLen))
402#ifdef CONFIG_DAWN_PROTO_WAKAAMA_DTLS_PSK
406 conn->dtlsContext, &conn->dtlsSession, buffer,
static_cast<size_t>(len));
411 owner.runtime->
handlePacket(buffer,
static_cast<size_t>(len), conn);
416 DAWNWARN(
"Wakaama packet from unknown peer\n");
422 if (len < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
424 DAWNERR(
"Wakaama recvfrom failed: %d\n", errno);
432 uint8_t buffer[CProtoWakaama::RX_BUFFER_SIZE];
439#ifdef CONFIG_DAWN_IO_NOTIFY
440 owner.processChangedResources();
443 if (owner.runtime ==
nullptr)
445 DAWNERR(
"Wakaama runtime gone; stopping client thread\n");
449 ret = owner.runtime->
step(&timeout);
455 DAWNERR(
"lwm2m_step failed: %d (continuing)\n", ret);
460 DAWNINFO(
"Wakaama: re-registering after registration failure\n");
464#ifdef DAWN_WAKAAMA_POLL_RECV
470 FD_SET(sockfd, &readfds);
473 tv.tv_sec = timeout > 0 ? timeout : 1;
475#ifdef DAWN_WAKAAMA_POLL_RECV
483 ret = select(sockfd + 1, &readfds,
nullptr,
nullptr, &tv);
484 if (ret > 0 && FD_ISSET(sockfd, &readfds))
486 receiveOne(buffer,
sizeof(buffer));
490 DAWNERR(
"Wakaama select failed: %d\n", errno);
493#ifdef DAWN_WAKAAMA_POLL_RECV
501int CProtoWakaama::initConnectionPool()
503 if (transport ==
nullptr)
505 transport =
new (std::nothrow)
Transport(*
this);
506 if (transport ==
nullptr)
515int CProtoWakaama::openSocket()
517 return transport ==
nullptr ? -ENODEV : transport->openSocket();
520int CProtoWakaama::initDtls()
522 return transport ==
nullptr ? -ENODEV : transport->initDtls();
525void CProtoWakaama::destroyDtls()
527 if (transport !=
nullptr)
529 transport->destroyDtls();
533void CProtoWakaama::closeAllConnections()
535 if (transport !=
nullptr)
537 transport->closeAllConnections();
541void CProtoWakaama::destroyConnectionPool()
547void CProtoWakaama::thread()
549 if (transport !=
nullptr)
555extern "C" uint8_t lwm2m_buffer_send(
void *sessionH, uint8_t *buffer,
size_t length,
void *userdata)
563 return COAP_500_INTERNAL_SERVER_ERROR;
566#ifdef CONFIG_DAWN_PROTO_WAKAAMA_DTLS_PSK
569 if (dtls_write(conn->dtlsContext, &conn->dtlsSession, buffer, length) < 0)
571 return COAP_500_INTERNAL_SERVER_ERROR;
574 return COAP_NO_ERROR;
579 conn->sock, buffer, length, 0,
reinterpret_cast<sockaddr *
>(&conn->addr), conn->addrLen) <
582 return COAP_500_INTERNAL_SERVER_ERROR;
585 return COAP_NO_ERROR;
588extern "C" bool lwm2m_session_is_equal(
void *session1,
void *session2,
void *userData)
592 return session1 == session2;
595extern "C" void *lwm2m_connect_server(uint16_t secObjInstID,
void *userData)
599 return transport ==
nullptr ? nullptr : transport->
connectServer(secObjInstID);
602extern "C" void lwm2m_close_connection(
void *sessionH,
void *userData)
607 if (transport !=
nullptr && conn !=
nullptr)
619 std::memset(conn, 0,
sizeof(*conn));
624#ifdef CONFIG_DAWN_PROTO_WAKAAMA_DTLS_PSK
625int dtlsSendToPeer(dtls_context_t *ctx, session_t *session, uint8 *data,
size_t len)
629 transport ==
nullptr ? nullptr : transport->
findConnection(&session->addr.st, session->size);
636 if (sendto(conn->sock, data, len, 0,
reinterpret_cast<sockaddr *
>(&conn->addr), conn->addrLen) <
642 conn->lastSend = lwm2m_gettime();
643 return static_cast<int>(len);
646int dtlsReadFromPeer(dtls_context_t *ctx, session_t *session, uint8 *data,
size_t len)
650 transport ==
nullptr ? nullptr : transport->
findConnection(&session->addr.st, session->size);
661int dtlsGetPskInfo(dtls_context_t *ctx,
662 const session_t *session,
663 dtls_credentials_type_t type,
664 const unsigned char *
id,
666 unsigned char *result,
671 transport ==
nullptr ? nullptr : transport->
findConnection(&session->addr.st, session->size);
672 const uint8_t *value =
nullptr;
680 return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
685 case DTLS_PSK_IDENTITY:
686 value = conn->pskIdentity;
687 valueLen = conn->pskIdentityLen;
691 value = conn->pskKey;
692 valueLen = conn->pskKeyLen;
699 return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
702 if (value ==
nullptr || valueLen == 0 || resultLen < valueLen)
704 return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
707 std::memcpy(result, value, valueLen);
708 return static_cast<int>(valueLen);
712bool parseServerUri(
const char *uri,
char *host,
size_t hostSize, uint16_t *port, uint8_t *scheme)
717 unsigned long parsedPort;
720 if (uri ==
nullptr || host ==
nullptr || hostSize == 0 || port ==
nullptr || scheme ==
nullptr)
725 if (std::strncmp(uri,
"coaps://", 8) == 0)
728 *scheme = CProtoWakaama::WAKAAMA_SERVER_SCHEME_COAPS;
730 else if (std::strncmp(uri,
"coap://", 7) == 0)
733 *scheme = CProtoWakaama::WAKAAMA_SERVER_SCHEME_COAP;
740 portSep = std::strrchr(begin,
':');
741 if (portSep ==
nullptr)
746 hostLen =
static_cast<size_t>(portSep - begin);
747 if (hostLen == 0 || hostLen >= hostSize)
752 if (hostLen >= 2 && begin[0] ==
'[' && begin[hostLen - 1] ==
']')
763 std::memcpy(host, begin, hostLen);
764 host[hostLen] =
'\0';
766 parsedPort = std::strtoul(portSep + 1, &end, 10);
767 if (*end !=
'\0' || parsedPort == 0 || parsedPort > UINT16_MAX)
772 *port =
static_cast<uint16_t
>(parsedPort);
776bool sockaddrEqual(
const sockaddr_storage &lhs,
778 const sockaddr_storage &rhs,
781 if (lhs.ss_family != rhs.ss_family)
786 switch (lhs.ss_family)
790 const sockaddr_in *lhs4 =
reinterpret_cast<const sockaddr_in *
>(&lhs);
791 const sockaddr_in *rhs4 =
reinterpret_cast<const sockaddr_in *
>(&rhs);
793 return lhsLen >=
sizeof(sockaddr_in) && rhsLen >=
sizeof(sockaddr_in) &&
794 lhs4->sin_port == rhs4->sin_port && lhs4->sin_addr.s_addr == rhs4->sin_addr.s_addr;
800 const sockaddr_in6 *lhs6 =
reinterpret_cast<const sockaddr_in6 *
>(&lhs);
801 const sockaddr_in6 *rhs6 =
reinterpret_cast<const sockaddr_in6 *
>(&rhs);
803 return lhsLen >=
sizeof(sockaddr_in6) && rhsLen >=
sizeof(sockaddr_in6) &&
804 lhs6->sin6_port == rhs6->sin6_port && lhs6->sin6_scope_id == rhs6->sin6_scope_id &&
805 std::memcmp(&lhs6->sin6_addr, &rhs6->sin6_addr,
sizeof(lhs6->sin6_addr)) == 0;
811 return lhsLen == rhsLen && std::memcmp(&lhs, &rhs, lhsLen) == 0;
Wakaama LwM2M client protocol implementation.
CThreadedObject & workerThread()
Get a reference to this thread controller.
bool recoverFromBootstrapWedge()
Nudge a registration-failed client (parked in STATE_BOOTSTRAP_REQUIRED with no bootstrap server) back...
security_instance_s * findSecurityInstance(uint16_t securityInstanceId) const
Find a Security object instance by instance ID.
int step(time_t *timeout)
Run one Wakaama state-machine step.
void handlePacket(uint8_t *buffer, size_t length, void *session)
Deliver one received packet into the Wakaama client context.
UDP and optional DTLS transport used by the Wakaama client runtime.
void closeAllConnections()
Close every active server connection.
int openSocket()
Open and bind the local UDP socket.
void handlePacket(Connection *conn, uint8_t *buffer, size_t length)
Deliver one received packet into the Wakaama runtime.
~Transport()
Close sockets, DTLS state, and connection-pool allocations.
Connection * findConnection(const void *addr, size_t addrLen)
Find an active connection by peer address.
int initDtls()
Initialize optional shared DTLS context state.
void destroyDtls()
Destroy optional shared DTLS context state.
void closeConnection(Connection *conn)
Close one active server connection.
Connection * connectServer(uint16_t securityInstanceId)
Connect to the server referenced by a Security object instance.
int initConnectionPool()
Allocate the fixed connection pool used by Wakaama callbacks.
void thread()
Run the transport receive loop.
Transport(CProtoWakaama &owner)
Construct transport state for a Wakaama protocol owner.
Out-of-tree user-extension hooks for Dawn.
Runtime state for one remote LwM2M server connection.
Runtime representation of one LwM2M Security object instance.