It is possible to miss a packet retransmission if it is just about to happen
and the coap_run_once() logic takes the time past the packet retransmission.
The retransmission will take place when coap_run_once() is next run, but that
could be significantly later.
1) Create a new coap_io_prepare_epoll() function that calls coap_write(), but
sets the etimer within this function instead of at the end of coap_run_once().
2) Handle the etimer trigger within coap_io_do_events() rather than loop
checking coap_io_do_events() within coap_run_once().
include/coap2/net.h:
Define coap_io_prepare_epoll().
Update coap_io_do_events() to be type void.
src/coap_io.c:
Add in coap_io_prepare_epoll().
Update coap_run_once() to use new/updated functions.
src/net.c:
Update coap_io_do_events().
Make coap_new_context() always fail if there is an epoll logic setup failure
libcoap compiled for epoll will not continue to run.
Give a runtime warning if coap_read() or coap_io_do_events() are called when
libcoap is compiled or not compiled respectively for using epoll.
[Both coap_read() and coap_io_do_events() need to be in libcoap-2.{map|sym} to
support when compiling with or without epoll support]
libcoap-2.map:
libcoap-2.sym:
Make coap_io_prepare_epoll() application accessible.
include/coap2/coap_dtls.h
include/coap2/coap_session.h
include/coap2/pdu.h
man/coap_encryption.txt.in
src/net.c
Revert out all the #define -> enum in #386 that require the enum to be
one byte long.
This is compiler version dependent when doing static inline expansions.
examples/client.c:
Make hex2char() just static.
examples/etsi_iot_01.c:
Make coap_add_payload() just static.
include/coap2/address.h:
src/address.c:
Make coap_address_init() an actual function.
libcoap-2.map:
libcoap-2.sym:
Expose coap_address_init() as a function.
Makefile.libcoap:
Include address.c as one of the libcoap files for Contiki.
examples/lwip/Makefile:
Include address.o as one of the libcoap files for LwIP.
include/coap2/coap_subscribe_internal.h and
include/coap2/coap_session_internal.h contain the information not exposed
to applications.
This internal information was moved out of the respective
include/coap2/subscribe.h and include/coap2/coap_session.h files comprising of
the coap_subscription_t and coap_endpoint_t definitions.
To ease inter .h file dependencies, a new file
include/coap2/coap_forward_decls.h is added to include forward declarations
for both libcoap and applications.
Makefile.am:
Include include/coap2/coap_*internal.h as files for distribution, but not
installed for applications to use.
Include include/coap2/coap_forward_decls.h, so it is installed for
applications.
include/coap2/coap.h.in:
Include "coap_forward_decls.h"
include/coap2/coap_forward_decls.h: (New)
Forward declare structures and typedef the structures defined in
coap_internal.h.
include/coap2/coap_internal.h: (New)
Includes all the required #include files that libcoap internally needs.
include/coap2/coap_session_internal.h: (New)
Include the coap_endpoint_t information taken from
include/coap2/coap_session.h.
include/coap2/coap_session.h:
Remove coap_endpoint_t definition.
include/coap2/lwippools.h:
Add in coap_forward_decls.h and coap_internal.h to handle the LwIP memory
allocations.
include/coap2/resource.h:
Add in "block.h" to allow things to build.
include/coap2/coap_subscribe_internal.h: (New)
Include the coap_subscription_t information taken from
include/coap2/subscribe.h.
include/coap2/subscribe.h:
Remove coap_subscription_t definition.
libcoap-2.map:
libcoap-2.sym:
Updated, removing coap_subscription_init().
src/address.c:
src/asyn.c:
src/block.c:
src/coap_debug.c:
src/coap_event.c:
src/coap_gnutls.c:
src/coap_hashkey.c:
src/coap_io.c:
src/coap_io_lwip.c:
src/coap_notls.c:
src/coap_openssl.c:
src/coap_session.c:
src/coap_time.c:
src/coap_tinydtls.c:
src/encode.c:
src/mem.c:
src/net.c:
src/option.c:
src/pdu.c:
src/resource.c:
src/str.c:
src/subscribe.c:
src/uri.c:
Add in '#include "coap_internal.h"' to the source files.
Remove other libcoap #include files.
win32/libcoap.vcxproj:
win32/libcoap.vcxproj.filters:
Update for Windows build (not tested).
examples/contiki/server.c:
src/coap_io.c:
Remove knowledge of endpoint from the server and update Contiki's
version of coap_run_once() to do the work instead.
====
Other structures (plus some internal required #defines) will follow as
separate PRs.
If epoll is available, use it within libcoap to speed things up, as well as
present to applications a file descriptor that can be used in a select() or
returned as an event in a epoll_wait() call.
Whenever coap_new_context() is called. set up a new epoll infrastructure with
epoll_create() and destroy it with coap_free_context().
For each new endpoint or session, add the socket fd to epoll. When the socket
is closed, remove the socket fd from epoll.
Whenever a socket wants a write, the appropriate epoll event gets EPOLLOUT
added and when the write happens reset the event back to just EPOLLIN.
As there may be a packet retry event sometime in the future, a timerfd is
created and added to the epoll structure (done in coap_new_context) which gets
freed off in coap_free_context. If coap_run_once() determines that there is
a future retry, timerfd is informed of when the event is to occur. When
timerfd times out, the application file descriptor will indicate a read is
available.
A new function coap_context_get_coap_fd(ctx) returns the application file
descriptor. The only other API change is that if wait_ms is set to 1 for
coap_run_once(ctx, wait_ms), there is no waiting for a new packet to arrive.
coap_run_once() is called, whether epoll support is available or not, to
process all the underlying required i/o.
If there is no epoll support, coap_run_once() continues to use the old
select() method, otherwise it will use epoll_wait(). If there is epoll
support, a modified version of coap_read() (coap_io_do_events()) is
used instead.
Makefile.am:
Do not expose coap_epoll_ctl_mod() and coap_io_do_events() to applications.
configure.ac:
Test for the presence of sys/epoll.h and sys/timerfd.h and if so setup
COAP_EPOLL_SUPPORT to be used for #ifdef testing.
Include new man page coap_io.txt.
examples/coap-server.c:
Include support for using this new exposed file descriptor.
include/coap2/coap_io.h:
Define COAP_MAX_EPOLL_EVENTS (to limit the number of events processed at once)
and include coap_endpoint_t and coap_session_t in coap_socket_t.
Include (internal) definition for coap_epoll_ctl_mod()
include/coap2/net.h:
Add epfd and eptimerfd to coap_context_t.
Define coap_context_get_coap_fd() for applications to use.
Include (internal) definition for coap_io_do_events().
Define COAP_RUN_BLOCK and COAP_RUN_NONBLOCK.
libcoap-2.map:
libcoap-2.sym:
Include coap_context_get_coap_fd.
configure.ac:
man/Makefile.am:
man/coap.txt.in:
man/coap_io.txt.in: (New)
Include the new coap_in(3) man page that documents coap_run_once() and
coap_context_get_coap_fd().
src/coap_io.c:
src/coap_openssl.c:
src/coap_session.c:
src/net.c:
Add in the epoll support.
src/net.c:
Add in coap_context_get_coap_fd() function.
As pointed out in issue #368 [1], C++ officially forbids aggregate
initialization for non-aggegrates. This change therefore introduces
an alternative version (which works for both C and C++) to provide
the required functionality.
str.c:
str.h:
Make coap_make_str_const() a function.
examples/coap-server.c:
Fix parameter type in coap_add_attr() call.
src/resource.c:
Static variables can no longer make use of coap_make_str_const().
libcoap-2.map:
libcoap-2.sym:
Make coap_make_str_const() appication visible.
Fixes#368.
[1] https://github.com/obgm/libcoap/issues/368
Update
As suggested in #292, several lists of #define could easily be defined
as enum instead - which allows for extra compiler checking.
The challenge was for where the (#define/enum) parameter defined in a
structure was one byte in size. Doxygen was picky and got confused, not
maintaining the fact that it was an enum if macros were used to differentiate
between using Microsoft or GCC when changing the enum size default from that
of an int.
include/coap2/coap_dtls.h:
include/coap2/coap_session.h:
include/coap2/pdu.h:
Use enums instead of #define where practical.
src/net.c:
Handle all the enum cases.
man/coap_encryption.txt.in:
man/coap_tls_library.txt.in:
Update man documentation.
As pointed out in issue #368 [1], C++ officially forbids aggregate
initialization for non-aggegrates. This change therefore introduces
a template function coap_make_str_const() that creates and initializes
an automatic variable that is moved to the caller on exit.
Note that at least -std=c++14 is required.
Fixes#368.
[1] https://github.com/obgm/libcoap/issues/368
Make endpoint->sessions and context->sessions a hashed chain of sessions
based on a key comprising of local and remote addresses and ports instead of
a linked list that has to be linearly searched.
include/coap2/coap_io.h:
include/coap2/coap_session.h:
Create a new struct coap_addr_tuple_t for the local and remote addresses/ports
to be used as the hash key. Update coap_packet_t and coap_session_t
appropriately.
Create SESSIONS_* macros to Add, Delete, Find and Iterate through the hashed
chain.
examples/coap-rd.c:
src/coap_io.c:
src/coap_io_lwip.c:
src/coap_openssl.c:
src/coap_session.c:
src/coap_tinydtls.c:
src/net.c:
src/resource.c:
Update with the new struct element names and replace the LL_* functions with
SESSIONS_* as appropriate.
Update coap_endpoint_get_session() to use SESSIONS_FIND() for fast lookup.
Future work is to review all the SESSIONS_ITER* and see if they can be sped up
as well.
As COAP_NACK_RST is used for
1. ICMP Port Unreachable message is received
2. CoAP reply RST message.
there needs to be a way to differentiate between these conditions.
If there is detected to be an ICMP response issue to sending a UDP
packet, report this as COAP_NACK_ICMP_ISSUE instead of COAP_NACK_RST.
If the reported issue to coap_session_disconnected() is COAP_NACK_ICMP_ISSUE,
then make sure that the packet (plain or DTLS) gets re-transmitted.
As hinted in #131 discussion, retry of the packet should be attempted,
(as per https://tools.ietf.org/html/rfc7252#section-4.2 ICMP Unreachable)
this fix will cause the plain packet to get retransmitted up to the maximum
retry count if type CON.
Likewise, DTLS will get restarted up to the maximum retry count if type CON.
examples/client.c:
Include COAP_NACK_ICMP_ISSUE in the nack_handler.
include/coap2/coap_io.h:
Add in COAP_NACK_ICMP_ISSUE to coap_nack_reason_t.
src/coap_session.c:
Update coap_session_disconnected() so that plain/DTLS can restart.
src/net.c:
Fix coap_wait_ack() to exponentially back off the re-transmit times.
Update coap_read_session() to report COAP_NACK_ICMP_ISSUE instead of
COAP_NACK_RST.
man/coap_handler.txt.in:
Update documentation
Add in support for both reliable and unreliable sessions when calling
coap_context_set_keepalive().
include/coap2/coap_session.h:
Track the last ping mid in coap_session_t.
src/coap_io.c:
src/coap_session.c:
Support both reliable and unreliable sending of pings in coap_write().
src/net.c:
Determine whether RST packet response is associated with a CoAP Empty Ping
that was sent and handle accordingly.
configure.ac:
examples/client.c:
include/coap2/net.h:
man/Makefile.am:
man/coap-client.txt.in:
man/coap.txt.in:
man/coap_handler.txt.in:
man/coap_keepalive.txt.in: (New)
man/coap_observe.txt.in:
Add in a new man page (coap_keepalive).
Update documentation to remove references to TCP and reliable only.
This is done by calling prng_init() with a non-zero value in coap_startup().
Change 7fb4ace2b7 modified the parameter to
prng_init() from a computed value to 0 when the code was re-organized.
This change uses the current time in microseconds as the seed to prng_init().
include/coap2/coap_time.h:
Add in coap_ticks_to_rt_us() support for lwIP and Contiki
src/net.c:
Update call to prng_init() in coap_startup() to use time in microsecs as the
seed parameter
See #373 where the issue was raised.
GnuTLS currently does not retry to send the Client Hello packets if there was
no (Server Hello) response. This was never correctly implemented.
src/coap_gnutls.c:
Fix receive_timout() so that it differentiates between DTLS and TLS.
If DTLS, if data has already been read in, indicate this.
if this is a coap_dtls_handle_timeout() call, only check readfds
(writefds will always be true, causing confusion).
Set DTLS timeouts where appropriate.
Update coap_dtls_*_timeout() functions to return the correct values.
include/coap2/coap_dtls.h:
src/coap_gnutls.c:
src/coap_io.c:
src/coap_notls.c:
src/coap_openssl.c:
src/coap_tinydtls.c:
Add in coap_tick_t now parameter to coap_dtls_get_timeout() for better
accuracy.
include/coap2/libcoap.h:
Simplify COAP_DEPRECATED definition to generically handle Windows
or other builds when setting the define for COAP_DEPRECATED.
Fixes#363
It appears that message_id was left in coap_context_t and coap_async_t
when coap_session_t was created.
include/coap2/async.h:
include/coap2/net.h:
Remove message_id.
src/net.c:
No longer just set message_id.
endpoint->hello is used to hold the 5 tuple etc. information about a new
connection that has come in. Once the DTLS handshake has been completed
a new coap_session_t is created for the ongoing encrypted work.
However, if a second new DTLS request comes in before the first session
has completed the DTLS handshakes, it will overwrite the endpoint->hello
information causing the first request to stall / get lost.
GnuTLS already has to check that the initial DTLS packet is a Client Hello
before starting up a GnuTLS session, so moving this logic up a level to
the initial DTLS packet makes sense to create a coap_session_t at this
point and getting rid of the temporary endpoint->hello gets rid of this
issue.
Makefile.am:
Replace coap_endpoint_new_dtls_session with coap_session_new_dtls_session
include/coap2/coap_session.h:
Remove coap_session_t hello;
Replace coap_endpoint_new_dtls_session with coap_session_new_dtls_session
src/coap_gnutls.c:
Remove references to endpoint->hello
Remove Client Hello check code (move it to coap_endpoint_get_session())
src/coap_io.c:
Remove references to freeing off ep->hello
src/coap_session.c:
Remove endpoint->hello code.
Check for Client Hello in coap_endpoint_get_session() if no session, is DTLS
and don't build coap_session if not a Client Hello.
Otherwise, always build a new coap_session.
Timeout Client Hello session if inactive to prevent a DoS.
Replace coap_endpoint_new_dtls_session with coap_session_new_dtls_session
src/coap_net.c:
Replace coap_endpoint_new_dtls_session with coap_session_new_dtls_session
Code works as expected for confirmable observes as the sent pdu is
retained for potential re-transmission.
For non-confirmable observes, the RST is seen in the response packet but
previously had no way of finding the observe/subscription to cancel.
Keep the last sent message id in coap_subscription_t (as tid) and when the
RST comes in, search for the subscription that has the same message id as
the pdu and remove that subscription.
include/coap2/subscribe.h:
Add in tid to coap_subscription_t (as per coap_pdu_t).
src/net.c:
Match tid in incoming RST pdu with a subscription. If found, delete the
subscription.
src/resource.c:
Save tid in both pdu and subscription when sending off an observe response.
To test code, make the following temporary change to coap-client
diff --git a/examples/client.c b/examples/client.c
index 4d3e4c7..0020b68 100644
--- a/examples/client.c
+++ b/examples/client.c
@@ -348,7 +348,8 @@ message_handler(struct coap_context_t *ctx,
#endif
/* check if this is a response to our original request */
- if (!check_token(received)) {
+static int count = 0;
+ if (!check_token(received) || ++count == 5) {
/* drop if this was just some message, or send RST in case of notification */
if (!sent && (received->type == COAP_MESSAGE_CON ||
received->type == COAP_MESSAGE_NON))
and run (coap-server running on the same host)
coap-client -N -s 10 coap://127.0.0.1/time
Adding in new structures to .h files that are typedef'd to not include
"struct" which are then referenced by other .h files can easily set up
recursive requirements that cannot be fulfilled.
In particular, adding in a new typedef'd struct to coap_dtls.h cannot
easily be used by coap_session.h
include/coap2/coap_dtls.h:
In coap_dtls.h, forward reference the (outside of the file) structures
and then prefix the coap_*_t entries with "struct", so making the
entries "struct coap_*_t" entries as appropriate
As coap-server now sets up 2 (or 4) endpoints, UDP and TCP based, all
subsequent endpoints are pre-pended to the ctx->endpoint list. Thus
ctx->endpoint will always be TCP based. Setting IPV6_JOIN_GROUP on a TCP
socket is a no-no.
As join() is complex function, it should be within libcoap so as to not require
detailed knowledge of how internal structures work (which really should be
opaque to the application writer).
include/coap2/net.h:
src/net.c:
libcoap-2.map:
libcoap-2.sym:
Create coap_join_mcast_group() in src/net.c (as it is a network layer
function using the (fixed) join() code. (Iterate through the ctx->endpoint list
looking for UDP based endpoints and only do the IPV6_JOIN_GROUP on them.)
There is no support for WITH_CONTIKI or WITH_LWIP at this point (returns -1).
examples/coap-server.c:
Remove join() code and replace call to join() with coap_join_mcast_group()
examples/coap-rd.c:
Remove join() code and replace call to join() with coap_join_mcast_group()
man/coap-rd.txt.in:
Correct a typo in the coap-ed man page
When there is a limited stack size, having large data items on the stack
can easily cause stack overflows.
This makes integration with any constrained stack O/S a lot easier.
include/coap2/coap_mutex.h:
Create coap_ mutex functions which are platform independent to protect
the (now) static variables if compiled with "-DCOAP_CONSTRAINED_STACK=1"
Makefile.am:
Include include/coap2/coap_mutex.h for distribution
coap_config.h.contiki:
coap_config.h.lwip:
As CONTIKI and LWIP have constrained stacks, set
"#define COAP_CONSTRAINED_STACK 1"
src/coap_debug.c:
src/coap_io.c:
src/net.c:
coap_show_pdu()
coap_log_impl()
coap_run_once()
coap_read_session()
coap_read_endpoint()
Move large data items out of the functions' stack and make them static
variables (defined within the function) if COAP_CONSTRAINED_STACK is defined.
Protect the usage of these static variables by mutexes.
Note: if "-DCOAP_CONSTRAINED_STACK=0" is used, then the stack will not
be constrained.
To reproduce the issue:
Server session
$ coap-server -v9 -n -k secretPSK -c server.pem -C ca.pem -R /etc/ssl/certs
Client session
$ examples/coap-client -v9 coaps://127.0.0.1/
include/coap2/coap_dtls.h:
src/coap_gnutls.c:
If neither coap_new_client_session_pki() or coap_new_client_session_psk()
is called, but coap_new_client_session() is (as done by coap-client if
no PKI or PSK options set), then set up a default PKI session to be
able to talk to a coap server.
By defining the CA file or Root CAs in the client, the server's certificate
can be validated by the options in coap_dtls_pki_t parameter even if the client
does not define a certificate.
[The server must be configured not to require a Client Cert]
examples/client.c:
Add in the support to the coap-client to allow any of Root CA, CA and Cert
to be defined to set up PKI checking.
src/coap_gnutls.c:
src/coap_openssl.c:
Remove the checks in the OpenSSL and GnuTLS underlying support that previously
used to enforce that both CA and Cert had to be set in the client.
Correct some of the PKI setup error messages to relect whether the failure is
on the client or the server.
include/coap2/coap_dtls.h:
src/coap_gnutls.c:
src/coap_notls.c:
src/coap_openssl.c:
src/coap_tinydtls.c:
Convert COAP_DTLS_ROLE_SERVER and COAP_DTLS_ROLE_CLIENT to enum
coap_dtls_role_t and use as appropriate to differentiate between the roles
in the different DTLS libraries
include/coap2/libcoap.h
_LIBCOAP_H_ to COAP_LIBCOAP_H_
src/coap_session.c
_COAP_SESSION_H_ to COAP_SESSION_C_
include/coap2/coap_session.h
_SESSION_H_ to COAP_SESSION_H_
otherwise, all #define _COAP_xxxxx wrappers are now COAP_xxxxx
Fixes#291
As str structure has been replaced by coap_string_t, remove it from the code
as it is no longer required, has been deprecated for some time and can cause
clashes with other str definitions.
Tidy up comment referring to str.
For any TLS implementation, it is possible to crash the coap-server by
initiating (D)TLS handshakes but never completing them, thus continuing to use
more RAM. Each partially completed handshake is left in the endpoint->sessions
chain in a state of COAP_SESSION_STATE_HANDSHAKE and unless
endpoint->max_idle_sessions is deliberately configured, these sessions will
never get deleted.
A cause of partially completed handshakes is when there is a PSK mismatch.
Furthermore, unnecessary CPU usage is used to search down long lists held in
endpoint->sessions.
This fix limits the number of these sessions in a handshake state to the
number defined by endpoint->max_handshake_sessions if non zero or 100 to prevent
memory exhaustion. By default, endpoint->max_handshake_sessions is 0.
Sessions in state COAP_SESSION_STATE_HANDSHAKE will get timed out after 30
seconds of inactivity.
This does not prevent a DOS attack by creating max_handshake_sessions as a
genuine new session will fail to get responded to until the number of current
COAP_SESSION_STATE_HANDSHAKE sessions drop. However, the use of Cookie
exchanges by the underlying TLS library prior to a new session being created
and added into endpoint->sessions will filter out a lot of the DOS attacks.
include/coap2/coap_session.h
New default definition for COAP_DEFAULT_MAX_HANDSHAKE_SESSIONS
New default definition for COAP_PARTIAL_SESSION_TIMEOUT_TICKS
include/coap2/net.h
Include max_handshake_sessions in coap_context_t
src/coap_session.c
In coap_endpoint_get_session() check the number of sessions in
COAP_SESSION_STATE_HANDSHAKE and handle accordingly when limits are hit, or
they need to be timed out.
Information set up in the pseudo session held in endpoint->hello is not freed
off when coap_free_endpoint() is called.
A solution is to split coap_session_free() into two parts with
coap_session_free() calling a new function coap_session_mfree().
Then coap_mfree_endpoint() can call coap_session_mfree() to free off the
endpoint specific hello information.
Makefile.am
Add coap_mfree_endpoint and coap_session_mfree to the CTAGS_IGNORE list.
libcoap-2.{map|sym}
Remove coap_mfree_endpoint as it is not a public function
include/coap2/coap_session.h
Add in new coap_session_mfree() function definition
src/coap_io.c
Call coap_session_mfree() from within coap_mfree_endpoint()
src/coap_session.c
Move some of the functionality out of coap_session_free() into a new
function coap_session_mfree() and call coap_session_mfree().
These new functions can be used to either update a buffer with, or coap_log()
out the current runtime TLS library along with the version of the library
that libcoap was built against.
include/coap2/debug.h
src/debug.c
libcoap-2.map
libcoap-2.sym
Support for the new functions coap_show_tls_version() and coap_string_tls_version().
This allows an application to determine what the built version is
as well as what the runtime version of a TLS library is.
include/coap2/coap_dtls.h
Add built_version to coap_tls_version_t
src/coap_openssl.c
src/coap_tinydtls.c
Additionally populate coap_tls_version_t with build_version when
coap_get_tls_library_version() is called.
src/coap_tinydtls.c
Correct the version returned as actual TinyDTLS version instead the
binary value of a DTLS packet type.