diff --git a/.gitignore b/.gitignore index 65dde4b1..2ee66671 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,7 @@ aclocal.m4 ar-lib autom4te.cache/ coap_config.h -coap_config.h.in +/coap_config.h.in compile config.* !config.yml diff --git a/CMakeLists.txt b/CMakeLists.txt index 606e1dd6..d50301b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -495,7 +495,6 @@ target_sources( ${CMAKE_CURRENT_LIST_DIR}/include/coap${LIBCOAP_API_VERSION}/block.h ${CMAKE_CURRENT_LIST_DIR}/include/coap${LIBCOAP_API_VERSION}/encode.h ${CMAKE_CURRENT_LIST_DIR}/include/coap${LIBCOAP_API_VERSION}/libcoap.h - ${CMAKE_CURRENT_LIST_DIR}/include/coap${LIBCOAP_API_VERSION}/lwippools.h ${CMAKE_CURRENT_LIST_DIR}/include/coap${LIBCOAP_API_VERSION}/mem.h ${CMAKE_CURRENT_LIST_DIR}/include/coap${LIBCOAP_API_VERSION}/net.h ${CMAKE_CURRENT_LIST_DIR}/include/coap${LIBCOAP_API_VERSION}/pdu.h @@ -699,7 +698,6 @@ install( PATTERN "*.h" PATTERN "coap.h" EXCLUDE PATTERN "coap_riot.h" EXCLUDE - PATTERN "lwippools.h" EXCLUDE PATTERN "utlist.h" EXCLUDE PATTERN "uthash.h" EXCLUDE PATTERN "*_internal.h" EXCLUDE) diff --git a/Makefile.am b/Makefile.am index dba0e612..9a6232ec 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,7 +28,7 @@ EXTRA_DIST = \ cmake/Config.cmake.in \ cmake/FindMbedTLS.cmake \ cmake/FindTinyDTLS.cmake \ - coap_config.h.lwip \ + coap_config.h.contiki \ coap_config.h.riot \ coap_config.h.windows \ libcoap-$(LIBCOAP_API_VERSION).pc.in \ @@ -36,6 +36,26 @@ EXTRA_DIST = \ libcoap-$(LIBCOAP_API_VERSION).sym \ examples/coap_list.h \ examples/getopt.c \ + examples/contiki/coap_config.h \ + examples/contiki/coap-observer.c \ + examples/contiki/Makefile \ + examples/contiki/Makefile.contiki \ + examples/contiki/radvd.conf.sample \ + examples/contiki/README \ + examples/contiki/server.c \ + examples/lwip/client.c \ + examples/lwip/client-coap.c \ + examples/lwip/client-coap.h \ + examples/lwip/Makefile \ + examples/lwip/README \ + examples/lwip/server.c \ + examples/lwip/server-coap.c \ + examples/lwip/server-coap.h \ + examples/lwip/config/coap_config.h \ + examples/lwip/config/coap_config.h.in \ + examples/lwip/config/lwipopts.h \ + examples/lwip/config/lwippools.h \ + Makefile.libcoap \ include/coap$(LIBCOAP_API_VERSION)/coap_internal.h \ include/coap$(LIBCOAP_API_VERSION)/coap_riot.h \ include/coap$(LIBCOAP_API_VERSION)/coap_asn1_internal.h \ @@ -53,9 +73,9 @@ EXTRA_DIST = \ include/coap$(LIBCOAP_API_VERSION)/coap.h.in \ include/coap$(LIBCOAP_API_VERSION)/coap.h.windows \ include/coap$(LIBCOAP_API_VERSION)/coap.h.windows.in \ - include/coap$(LIBCOAP_API_VERSION)/lwippools.h \ include/coap$(LIBCOAP_API_VERSION)/uthash.h \ include/coap$(LIBCOAP_API_VERSION)/utlist.h \ + src/coap_io_lwip.c \ src/coap_io_riot.c \ tests/test_error_response.h \ tests/test_encode.h \ diff --git a/configure.ac b/configure.ac index 5c46cc6e..ff7e693f 100644 --- a/configure.ac +++ b/configure.ac @@ -949,11 +949,11 @@ CFLAGS="$CFLAGS $ADDITIONAL_CFLAGS" # AC_CONFIG_FILES([ Makefile -coap_config.h.lwip coap_config.h.riot coap_config.h.windows doc/Makefile examples/Makefile +examples/lwip/config/coap_config.h include/coap$LIBCOAP_API_VERSION/coap.h include/coap$LIBCOAP_API_VERSION/coap.h.windows man/coap.txt @@ -969,6 +969,7 @@ man/coap_handler.txt man/coap_io.txt man/coap_keepalive.txt man/coap_logging.txt +man/coap_lwip.txt man/coap_observe.txt man/coap_pdu_access.txt man/coap_pdu_setup.txt diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 782410d8..401c49f3 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -904,7 +904,6 @@ RECURSIVE = NO EXCLUDE = @top_srcdir@/src/coap_io_lwip.c \ @top_srcdir@/src/coap_io_riot.c \ - @top_srcdir@/include/coap@LIBCOAP_API_VERSION@/lwippools.h \ @top_srcdir@/include/coap@LIBCOAP_API_VERSION@/uthash.h \ @top_srcdir@/include/coap@LIBCOAP_API_VERSION@/utlist.h diff --git a/examples/lwip/.gitignore b/examples/lwip/.gitignore index 7905765c..a8e0bd03 100644 --- a/examples/lwip/.gitignore +++ b/examples/lwip/.gitignore @@ -4,4 +4,5 @@ lwip-contrib # never objects, and not the resulting binary *.o +client server diff --git a/examples/lwip/Makefile b/examples/lwip/Makefile index 55daeec9..b3e4a484 100644 --- a/examples/lwip/Makefile +++ b/examples/lwip/Makefile @@ -1,17 +1,29 @@ -top_srcdir = ../.. -include_dir = $(top_srcdir)/include +# Makefile +# +# Copyright (C) 2010-2022 Olaf Bergmann and others +# +# SPDX-License-Identifier: BSD-2-Clause +# +# This file is part of the CoAP C library libcoap. Please see README and +# COPYING for terms of use. -LIBCOAP_API_VERSION = $(shell sed -ne 's/^LIBCOAP_API_VERSION=\([0-9]\+\).*$$/\1/p' $(top_srcdir)/configure.ac) +libcoap_dir = ../../ -coap_include_dir = $(include_dir)/coap$(LIBCOAP_API_VERSION) +LIBCOAP_API_VERSION = $(shell if [ -f $(libcoap_dir)/configure.ac ]; then \ + sed -ne 's/^LIBCOAP_API_VERSION=\([0-9]\+\).*$$/\1/p' $(libcoap_dir)/configure.ac; \ + else echo -n "3"; fi) -WITH_LWIP_BRANCH=STABLE-2_0_3_RELEASE -WITH_LWIP_CONTRIB_BRANCH=STABLE-2_0_1_RELEASE +coap_include_dir = $(libcoap_dir)/include/coap$(LIBCOAP_API_VERSION) + +WITH_LWIP_BRANCH=STABLE-2_1_3_RELEASE +WITH_LWIP_CONTRIB_BRANCH=STABLE-2_1_0_RELEASE # Need to determine which library clock_gettime() resides in (as found by ./autogen.sh) -LDLIBS := $(shell if [ ../../config.log ] ; then grep ac_cv_search_clock_gettime=- ../../config.log|cut -d= -f2 ; fi) +LDLIBS := $(shell if [ -f $(libcoap_dir)/config.log ] ; then \ + grep ac_cv_search_clock_gettime=- $(libcoap_dir)/config.log|cut -d= -f2 ; fi) -all: server +all: lib-server lib-client lwip lwip-contrib check-version \ + $(coap_include_dir)/coap.h server client lwip: git clone --depth 1 git://git.savannah.nongnu.org/lwip.git -b $(WITH_LWIP_BRANCH) @@ -23,47 +35,119 @@ lwip-contrib: (cd lwip-contrib ; git checkout $(WITH_LWIP_CONTRIB_BRANCH)) $(MAKE) +check-version: + @(if [ -d lwip ] ; then \ + cd lwip ; \ + TAG=`git describe --tags --all`; \ + if [ "$$TAG" != ${WITH_LWIP_BRANCH} ] ; then \ + if [ "$$TAG" != "tags/${WITH_LWIP_BRANCH}" ] ; then \ + echo "Updating lwip to ${WITH_LWIP_BRANCH}" ; \ + cd .. ; \ + rm -rf lwip ; \ + ${MAKE}; \ + fi ; \ + fi ; \ + fi) + @(if [ -d lwip-contrib ] ; then \ + cd lwip-contrib ; \ + TAG=`git describe --tags`; \ + if [ "$$TAG" != ${WITH_LWIP_CONTRIB_BRANCH} ] ; then \ + if [ "$$TAG" != "tags/${WITH_LWIP_CONTRIB_BRANCH}" ] ; then \ + echo "Updating lwip-contib to ${WITH_LWIP_CONTRIB_BRANCH}" ; \ + cd .. ; \ + rm -rf lwip-contrib ; \ + ${MAKE}; \ + fi ; \ + fi ; \ + fi) + # lwip and coap opts (include early to shadow the lwip-contrib/ports/unix/proj/minimal/ file and any ../../config.h) -CFLAGS += -DWITH_LWIP -iquote. +CFLAGS += -DWITH_LWIP -iquote./config # lwip library -LWIPOBJS = def.o init.o tapif.o etharp.o netif.o timeouts.o stats.o mem.o memp.o udp.o tcp.o pbuf.o ip4_addr.o ip4.o inet_chksum.o tcp_in.o tcp_out.o icmp.o raw.o ip4_frag.o sys_arch.o ethernet.o ip.o -vpath %.c lwip/src/core/ lwip-contrib/ports/unix/proj/minimal/ lwip/src/netif/ lwip/src/core/ipv4/ lwip-contrib/ports/unix/port/ lwip-contrib/ports/unix/port/netif/ +LWIP_SRC = def.c init.c tapif.c etharp.c netif.c timeouts.c stats.c udp.c \ + tcp.c pbuf.c ip4_addr.c ip4.c inet_chksum.c tcp_in.c tcp_out.c \ + icmp.c raw.c ip4_frag.c sys_arch.c ethernet.c ip.c mem.c memp.c +vpath %.c lwip/src/core/ lwip-contrib/ports/unix/proj/minimal/ \ + lwip/src/netif/ lwip/src/core/ipv4/ lwip-contrib/ports/unix/port/ \ + lwip-contrib/ports/unix/port/netif/ # CFLAGS += -DLWIP_UNIX_LINUX # if ipv6 is used vpath %.c lwip/src/core/ipv6/ -LWIPOBJS += mld6.o ip6.o icmp6.o ethip6.o nd6.o ip6_addr.o ip6_frag.o +LWIP_SRC += mld6.c ip6.c icmp6.c ethip6.c nd6.c ip6_addr.c ip6_frag.c +C_LWIP_OBJ =$(patsubst %.c,lib-client/%.o,$(LWIP_SRC)) +S_LWIP_OBJ =$(patsubst %.c,lib-server/%.o,$(LWIP_SRC)) # coap library CFLAGS += -std=gnu99 -# set path to coap_include_dir for lwippools.h -CFLAGS += -I$(top_srcdir)/include -I$(coap_include_dir) +CFLAGS += -I$(libcoap_dir)/include -vpath %.c $(top_srcdir)/src +vpath %.c $(libcoap_dir)/src -COAPOBJS = net.o coap_cache.o coap_debug.o coap_option.o resource.o pdu.o encode.o coap_subscribe.o coap_io_lwip.o block.o uri.o str.o coap_session.o coap_notls.o coap_hashkey.o coap_address.o coap_tcp.o coap_async.o +COAP_SRC = coap_address.c \ + coap_async.c \ + block.c \ + coap_cache.c \ + coap_debug.c \ + encode.c \ + coap_hashkey.c \ + coap_io.c \ + coap_io_lwip.c \ + net.c \ + coap_notls.c \ + coap_option.c \ + pdu.c \ + resource.c \ + coap_session.c \ + coap_subscribe.c \ + str.c \ + coap_tcp.c \ + uri.c + +C_COAP_OBJ =$(patsubst %.c,lib-client/%.o,$(COAP_SRC)) +S_COAP_OBJ =$(patsubst %.c,lib-server/%.o,$(COAP_SRC)) CFLAGS += -g3 -Wall -Wextra -pedantic -O0 # not sorted out yet CFLAGS += -Wno-unused-parameter -CFLAGS += -Ilwip/src/include/ -Ilwip/src/include/ipv4/ -Ilwip-contrib/ports/unix/port/include/ -Ilwip-contrib/ports/unix/proj/minimal/ +CFLAGS += -Ilwip/src/include/ -Ilwip/src/include/ipv4/ \ + -Ilwip-contrib/ports/unix/port/include/ \ + -Ilwip-contrib/ports/unix/proj/minimal/ -OBJS = server.o server-coap.o ${LWIPOBJS} ${COAPOBJS} +SOBJS = server.o server-coap.o ${S_LWIP_OBJ} ${S_COAP_OBJ} + +COBJS = client.o client-coap.o ${C_LWIP_OBJ} ${C_COAP_OBJ} $(coap_include_dir)/coap.h: @echo "Error: $@ not present. Run the autotools chain (\`./autogen.sh && ./configure\`) in the project root directory to build the required coap.h file." @exit 1 -${OBJS}: lwip lwip-contrib $(coap_include_dir)/coap.h +${SOBJS}: $(coap_include_dir)/coap.h server-coap.h -server: ${OBJS} +server: ${SOBJS} + +${COBJS}: $(coap_include_dir)/coap.h client-coap.h + +client: ${COBJS} + +lib-server: + @mkdir -p $@ + +lib-server/%.o: %.c config/lwipopts.h config/lwippools.h config/coap_config.h + $(CC) ${CFLAGS} -DCOAP_SERVER_SUPPORT -c $< -o $@ + +lib-client: + @mkdir -p $@ + +lib-client/%.o: %.c config/lwipopts.h config/lwippools.h config/coap_config.h + $(CC) ${CFLAGS} -DCOAP_CLIENT_SUPPORT -c $< -o $@ clean: - rm -f ${OBJS} + rm -rf server client ${COBJS} ${SOBJS} lib-server lib-client -.PHONY: clean +.PHONY: all clean diff --git a/examples/lwip/README b/examples/lwip/README index c53695f7..76e8276e 100644 --- a/examples/lwip/README +++ b/examples/lwip/README @@ -1,7 +1,7 @@ Example of libcoap running on lwIP ================================== -To run the example, do +To run the server example, do $ make $ sudo ./server @@ -11,7 +11,7 @@ To run the example, do $ sudo ip a a dev tap0 192.168.113.1/24 and query `coap://192.168.113.2/time?ticks` with any coap tool, -or query `coap://[aaaa::1000]/.well-known/core` +or query `coap://192.168.113.2/.well-known/core` This will @@ -23,8 +23,31 @@ This will * return the appropriate response from the server to the client. +The server supports the "-v level" option where logging "level" can be 0 to 7. + The server creates a resource for 'time' with a query 'ticks'. This is reported for `.well-known/core`. The work flow for adding more resources does not differ from regular libcoap usage. If you seem to run out of memory creating the resources, tweak the number of pre-allocated resources -in `include/coap/lwippools.h`. +in `config/lwippools.h`. + +To run the client example + + $ make + $ sudo ./client + +As this tries to connect to coap://libcoap.net/, the tap0 interface will need IP +forwarding enabled + + $ sudo sysctl -w net.ipv4.conf.default.forwarding=1 + +As well as the interface connecting to the internet will need a NAT rule to +masquerade the internal IP address (192.168.114.2) to the IP of the outgoing +interface (where eth0 is replaced by your public facing interface name) + + $ iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE + +The client supports the "-v level" option where logging "level" can be 0 to 7. +The client supports an optional parameter which is the CoAP URI to connect to +.e.g "coap://libcoap.net/.well-known/core". The default +is "coap://libcoap.net/". diff --git a/examples/lwip/client-coap.c b/examples/lwip/client-coap.c new file mode 100644 index 00000000..ae2aa4eb --- /dev/null +++ b/examples/lwip/client-coap.c @@ -0,0 +1,226 @@ +/* + * client-coap.c -- LwIP example + * + * Copyright (C) 2013-2016 Christian Amsüss + * Copyright (C) 2018-2022 Jon Shallow + * + * SPDX-License-Identifier: BSD-2-Clause + * + * This file is part of the CoAP library libcoap. Please see README for terms + * of use. + */ + +#include "coap_config.h" +#include +#include +#include +#include +#include "client-coap.h" + +static coap_context_t *main_coap_context = NULL; +static coap_optlist_t *optlist = NULL; + +static int quit = 0; + +static coap_response_t +message_handler(coap_session_t *session, + const coap_pdu_t *sent, + const coap_pdu_t *received, + const coap_mid_t id) { + const uint8_t *data; + size_t len; + size_t offset; + size_t total; + + if (coap_get_data_large(received, &len, &data, &offset, &total)) { + printf("%*.*s", (int)len, (int)len, (const char*)data); + if (len + offset == total) { + printf("\n"); + quit = 1; + } + } + return COAP_RESPONSE_OK; +} + +static void +nack_handler(coap_session_t *session COAP_UNUSED, + const coap_pdu_t *sent COAP_UNUSED, + const coap_nack_reason_t reason, + const coap_mid_t id COAP_UNUSED) { + + switch(reason) { + case COAP_NACK_TOO_MANY_RETRIES: + case COAP_NACK_NOT_DELIVERABLE: + case COAP_NACK_RST: + case COAP_NACK_TLS_FAILED: + coap_log(LOG_ERR, "cannot send CoAP pdu\n"); + quit = 1; + break; + case COAP_NACK_ICMP_ISSUE: + default: + ; + } + return; +} + +static int +resolve_address(const char *host, const char *service, coap_address_t *dst) { + + struct addrinfo *res, *ainfo; + struct addrinfo hints; + int error, len=-1; + struct sockaddr_in *sock4; + struct sockaddr_in6 *sock6; + + memset(&hints, 0, sizeof(hints)); + memset(dst, 0, sizeof(*dst)); + hints.ai_socktype = SOCK_DGRAM; + hints.ai_family = AF_UNSPEC; + + error = getaddrinfo(host, service, &hints, &res); + + if (error != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error)); + return error; + } + + for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) { + switch (ainfo->ai_family) { + case AF_INET: + sock4 = (struct sockaddr_in *)ainfo->ai_addr; + dst->port = ntohs(sock4->sin_port); + len = ainfo->ai_addrlen; + memcpy(&dst->addr, &sock4->sin_addr, 4); + dst->addr.type = IPADDR_TYPE_V4; + goto finish; + case AF_INET6: + sock6 = (struct sockaddr_in6 *)ainfo->ai_addr; + dst->port = ntohs(sock6->sin6_port); + len = ainfo->ai_addrlen; + memcpy(&dst->addr, &sock6->sin6_addr, 16); + dst->addr.type = IPADDR_TYPE_V6; + goto finish; + default: + ; + } + } + + finish: + freeaddrinfo(res); + return len; +} + +void +client_coap_init(coap_lwip_input_wait_handler_t input_wait, void *input_arg, + int log_level, const char* do_uri) +{ + coap_session_t *session = NULL; + coap_pdu_t *pdu; + coap_address_t dst; + coap_mid_t mid; + int len; + coap_uri_t uri; + char portbuf[8]; +#define BUFSIZE 100 + unsigned char _buf[BUFSIZE]; + unsigned char *buf = _buf; + size_t buflen; + int res; + + coap_set_log_level(log_level); + + /* Parse the URI */ + len = coap_split_uri((const unsigned char *)do_uri, strlen(do_uri), &uri); + LWIP_ASSERT("Failed to parse uri", len == 0); + LWIP_ASSERT("Unsupported URI type", uri.scheme == COAP_URI_SCHEME_COAP); + + snprintf(portbuf, sizeof(portbuf), "%d", uri.port); + snprintf((char *)_buf, sizeof(_buf), "%*.*s", (int)uri.host.length, + (int)uri.host.length, (const char *)uri.host.s); + /* resolve destination address where server should be sent */ + len = resolve_address((const char*)_buf, portbuf, &dst); + LWIP_ASSERT("Failed to resolve address", len > 0); + + main_coap_context = coap_new_context(NULL); + LWIP_ASSERT("Failed to initialize context", main_coap_context != NULL); + + coap_context_set_block_mode(main_coap_context, COAP_BLOCK_USE_LIBCOAP); + coap_lwip_set_input_wait_handler(main_coap_context, input_wait, input_arg); + + session = coap_new_client_session(main_coap_context, NULL, &dst, + COAP_PROTO_UDP); + + LWIP_ASSERT("Failed to create session", session != NULL); + + coap_register_response_handler(main_coap_context, message_handler); + coap_register_nack_handler(main_coap_context, nack_handler); + + /* construct CoAP message */ + pdu = coap_pdu_init(COAP_MESSAGE_CON, + COAP_REQUEST_CODE_GET, + coap_new_message_id(session), + coap_session_max_pdu_size(session)); + LWIP_ASSERT("Failed to create PDU", pdu != NULL); + + if (uri.port != COAP_DEFAULT_PORT) { + coap_insert_optlist(&optlist, + coap_new_optlist(COAP_OPTION_URI_PORT, + coap_encode_var_safe(_buf, 4, + (uri.port & 0xffff)), + _buf)); + } + if (uri.path.length) { + buflen = BUFSIZE; + if (uri.path.length > BUFSIZE) + coap_log(LOG_WARNING, "URI path will be truncated (max buffer %d)\n", BUFSIZE); + res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen); + + while (res--) { + coap_insert_optlist(&optlist, + coap_new_optlist(COAP_OPTION_URI_PATH, + coap_opt_length(buf), + coap_opt_value(buf))); + + buf += coap_opt_size(buf); + } + } + + if (uri.query.length) { + buflen = BUFSIZE; + buf = _buf; + res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen); + + while (res--) { + coap_insert_optlist(&optlist, + coap_new_optlist(COAP_OPTION_URI_QUERY, + coap_opt_length(buf), + coap_opt_value(buf))); + + buf += coap_opt_size(buf); + } + } + + /* Add option list (sorted) to the PDU */ + if (optlist) { + res = coap_add_optlist_pdu(pdu, &optlist); + LWIP_ASSERT("Failed to add options to PDU", res == 1); + } + + /* and send the PDU */ + mid = coap_send(session, pdu); + LWIP_ASSERT("Failed to send PDU", mid != COAP_INVALID_MID); +} + +void +client_coap_finished(void) { + coap_delete_optlist(optlist); + coap_free_context(main_coap_context); + main_coap_context = NULL; +} + +int +client_coap_poll(void) +{ + coap_io_process(main_coap_context, 1000); + return quit; +} diff --git a/examples/lwip/client-coap.h b/examples/lwip/client-coap.h new file mode 100644 index 00000000..5abd9094 --- /dev/null +++ b/examples/lwip/client-coap.h @@ -0,0 +1,29 @@ +/* + * server-coap.h -- LwIP example + * + * Copyright (C) 2013-2016 Christian Amsüss + * Copyright (C) 2022 Jon Shallow + * + * SPDX-License-Identifier: BSD-2-Clause + * + * This file is part of the CoAP library libcoap. Please see README for terms + * of use. + */ + +#include "coap_config.h" +#include + +/* Start up the CoAP Client */ +void client_coap_init(coap_lwip_input_wait_handler_t input_wait, void *input_arg, + coap_log_t log_level, const char* do_uri); + +/* Close down CoAP activity */ + +void client_coap_finished(void); + +/* + * Call this when you think that work needs to be done + * + * Returns: 1 if there is no more work to be done, else 0 + */ +int client_coap_poll(void); diff --git a/examples/lwip/client.c b/examples/lwip/client.c new file mode 100644 index 00000000..693e7935 --- /dev/null +++ b/examples/lwip/client.c @@ -0,0 +1,157 @@ +/* + * Demo for libcoap on lwIP + * + * partially copied from lwip-contrib/ports/unix/proj/minimal/main.c + * + * + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * Author: Adam Dunkels + * RT timer modifications by Christiaan Simons + * lwip adaptions: chrysn + * also, https://savannah.nongnu.org/bugs/?40245 was applied */ + +#include "client-coap.h" + +#include +#include + +#include +#include + +#include + +static ip4_addr_t ipaddr, netmask, gw; +static int quit = 0; + +#ifndef COAP_URI +#define COAP_URI "coap://libcoap.net" +#endif /* COAP_URI */ + +static void +handle_sigint(int signum) { + (void)signum; + + client_coap_finished(); + printf("Client Application finished.\n"); + exit(0); +} + +static int +wait_for_input(void *arg, uint32_t milli_secs) { + struct netif *netif = (struct netif *)arg; + int ret; + + (void)milli_secs; + ret = tapif_select(netif); + + sys_check_timeouts(); + return ret; +} + +int +main(int argc, char **argv) +{ + struct netif netif; +#ifndef _WIN32 + struct sigaction sa; +#endif + int no_more = 0; + int opt; + int log_level = 4; /* LOG_WARNING */ + const char *use_uri = COAP_URI; + + while ((opt = getopt(argc, argv, "v:")) != -1) { + switch (opt) { + case 'v': + log_level = atoi(optarg); + break; + default: + exit(1); + } + } + + if (optind < argc) { + use_uri = argv[optind]; + } + + /* startup defaults (may be overridden by one or more opts). this is + * hard-coded v4 even in presence of v6, which does auto-discovery and + * should thus wind up with an address of fe80::12:34ff:fe56:78ab%tap0 + * */ + IP4_ADDR(&gw, 192,168,114,1); + IP4_ADDR(&ipaddr, 192,168,114,2); + IP4_ADDR(&netmask, 255,255,255,0); + + lwip_init(); + + printf("TCP/IP initialized.\n"); + + netif_add(&netif, &ipaddr, &netmask, &gw, NULL, tapif_init, ethernet_input); + netif.flags |= NETIF_FLAG_ETHARP; + netif_set_default(&netif); + netif_set_up(&netif); +#if LWIP_IPV6 + netif_create_ip6_linklocal_address(&netif, 1); +#endif + + /* start applications here */ + +#ifdef _WIN32 + signal(SIGINT, handle_sigint); +#else + memset (&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = handle_sigint; + sa.sa_flags = 0; + sigaction (SIGINT, &sa, NULL); + sigaction (SIGTERM, &sa, NULL); + /* So we do not exit on a SIGPIPE */ + sa.sa_handler = SIG_IGN; + sigaction (SIGPIPE, &sa, NULL); +#endif + + client_coap_init(wait_for_input, &netif, log_level, use_uri); + + printf("Client Application started.\n"); + + while (!quit && !no_more) { + /* + * Poll netif, pass any read packet to lwIP + * Has internal timeout of 100 msec (sometimes less) based on + * sys_timeouts_sleeptime(). + */ + tapif_select(&netif); + + sys_check_timeouts(); + + no_more = client_coap_poll(); + } + + client_coap_finished(); + printf("Client Application finished.\n"); + return 0; +} diff --git a/examples/lwip/coap_config.h b/examples/lwip/coap_config.h deleted file mode 120000 index 3bb8c3a1..00000000 --- a/examples/lwip/coap_config.h +++ /dev/null @@ -1 +0,0 @@ -../../coap_config.h.lwip \ No newline at end of file diff --git a/coap_config.h.lwip b/examples/lwip/config/coap_config.h similarity index 97% rename from coap_config.h.lwip rename to examples/lwip/config/coap_config.h index 265e8923..e71ccb01 100644 --- a/coap_config.h.lwip +++ b/examples/lwip/config/coap_config.h @@ -39,4 +39,6 @@ #define HAVE_LIMITS_H +#define HAVE_SNPRINTF + #endif /* COAP_CONFIG_H_ */ diff --git a/coap_config.h.lwip.in b/examples/lwip/config/coap_config.h.in similarity index 97% rename from coap_config.h.lwip.in rename to examples/lwip/config/coap_config.h.in index 2fcf3cb6..5d4c7609 100644 --- a/coap_config.h.lwip.in +++ b/examples/lwip/config/coap_config.h.in @@ -39,4 +39,6 @@ #define HAVE_LIMITS_H +#define HAVE_SNPRINTF + #endif /* COAP_CONFIG_H_ */ diff --git a/examples/lwip/lwipopts.h b/examples/lwip/config/lwipopts.h similarity index 71% rename from examples/lwip/lwipopts.h rename to examples/lwip/config/lwipopts.h index 2a86b855..1e417254 100644 --- a/examples/lwip/lwipopts.h +++ b/examples/lwip/config/lwipopts.h @@ -10,8 +10,6 @@ * of use. */ -typedef unsigned int sys_prot_t; - #define NO_SYS 1 #define LWIP_SOCKET (NO_SYS==0) #define LWIP_NETCONN (NO_SYS==0) @@ -28,10 +26,14 @@ typedef unsigned int sys_prot_t; #define netif_get_index(netif) ((u8_t)((netif)->num + 1)) #endif -#if NO_SYS -#include -extern pthread_mutex_t lwprot_mutex; -extern pthread_t lwprot_thread; -extern int lwprot_count; -#endif #define MEMP_USE_CUSTOM_POOLS 1 +#define MEM_SIZE (4 * 1024) + +/* Set if space is to be reserved for a response PDU */ +#define MEMP_STATS 1 + +/* + * Set to display (with LOG_DEBUG) custom pools information + * (Needs MEMP_STATS set) when coap_free_context() is called. + */ +#define LWIP_STATS_DISPLAY 1 diff --git a/include/coap3/lwippools.h b/examples/lwip/config/lwippools.h similarity index 82% rename from include/coap3/lwippools.h rename to examples/lwip/config/lwippools.h index 4b1dd6fe..1c8e2ccf 100644 --- a/include/coap3/lwippools.h +++ b/examples/lwip/config/lwippools.h @@ -11,10 +11,10 @@ * include this or include something more generic which includes this), and * MEMP_USE_CUSTOM_POOLS has to be set in lwipopts.h. */ -#include "coap_internal.h" -#include "net.h" -#include "resource.h" -#include "coap_subscribe.h" +#include "coap3/coap_internal.h" +#include "coap3/net.h" +#include "coap3/resource.h" +#include "coap3/coap_subscribe.h" #ifndef MEMP_NUM_COAPCONTEXT #define MEMP_NUM_COAPCONTEXT 1 @@ -30,7 +30,7 @@ #endif #ifndef MEMP_NUM_COAPNODE -#define MEMP_NUM_COAPNODE 4 +#define MEMP_NUM_COAPNODE 5 #endif #ifndef MEMP_NUM_COAPPDU @@ -54,7 +54,7 @@ #endif #ifndef MEMP_NUM_COAPOPTLIST -#define MEMP_NUM_COAPOPTLIST 1 +#define MEMP_NUM_COAPOPTLIST 5 #endif #ifndef MEMP_LEN_COAPOPTLIST @@ -82,7 +82,7 @@ #endif #ifndef MEMP_LEN_COAPPDUBUF -#define MEMP_LEN_COAPPDUBUF 32 +#define MEMP_LEN_COAPPDUBUF 400 #endif #ifndef MEMP_NUM_COAPLGXMIT @@ -97,21 +97,35 @@ #define MEMP_NUM_COAPLGSRCV 2 #endif +#ifndef MEMP_NUM_COAPDIGESTCTX +#define MEMP_NUM_COAPDIGESTCTX 4 +#endif + LWIP_MEMPOOL(COAP_CONTEXT, MEMP_NUM_COAPCONTEXT, sizeof(coap_context_t), "COAP_CONTEXT") +#ifdef COAP_SERVER_SUPPORT LWIP_MEMPOOL(COAP_ENDPOINT, MEMP_NUM_COAPENDPOINT, sizeof(coap_endpoint_t), "COAP_ENDPOINT") +#endif /* COAP_SERVER_SUPPORT */ LWIP_MEMPOOL(COAP_PACKET, MEMP_NUM_COAPPACKET, sizeof(coap_packet_t), "COAP_PACKET") LWIP_MEMPOOL(COAP_NODE, MEMP_NUM_COAPNODE, sizeof(coap_queue_t), "COAP_NODE") LWIP_MEMPOOL(COAP_PDU, MEMP_NUM_COAPPDU, sizeof(coap_pdu_t), "COAP_PDU") LWIP_MEMPOOL(COAP_SESSION, MEMP_NUM_COAPSESSION, sizeof(coap_session_t), "COAP_SESSION") -LWIP_MEMPOOL(COAP_subscription, MEMP_NUM_COAP_SUBSCRIPTION, sizeof(coap_subscription_t), "COAP_subscription") +#ifdef COAP_SERVER_SUPPORT +LWIP_MEMPOOL(COAP_subscription, MEMP_NUM_COAP_SUBSCRIPTION, sizeof(coap_subscription_t), "COAP_SUBSCRIPTION") LWIP_MEMPOOL(COAP_RESOURCE, MEMP_NUM_COAPRESOURCE, sizeof(coap_resource_t), "COAP_RESOURCE") LWIP_MEMPOOL(COAP_RESOURCEATTR, MEMP_NUM_COAPRESOURCEATTR, sizeof(coap_attr_t), "COAP_RESOURCEATTR") +#endif /* COAP_SERVER_SUPPORT */ LWIP_MEMPOOL(COAP_OPTLIST, MEMP_NUM_COAPOPTLIST, sizeof(coap_optlist_t)+MEMP_LEN_COAPOPTLIST, "COAP_OPTLIST") LWIP_MEMPOOL(COAP_STRING, MEMP_NUM_COAPSTRING, sizeof(coap_string_t)+MEMP_LEN_COAPSTRING, "COAP_STRING") +#ifdef COAP_SERVER_SUPPORT LWIP_MEMPOOL(COAP_CACHE_KEY, MEMP_NUM_COAPCACHE_KEYS, sizeof(coap_cache_key_t), "COAP_CACHE_KEY") LWIP_MEMPOOL(COAP_CACHE_ENTRY, MEMP_NUM_COAPCACHE_ENTRIES, sizeof(coap_cache_entry_t), "COAP_CACHE_ENTRY") +#endif /* COAP_SERVER_SUPPORT */ LWIP_MEMPOOL(COAP_PDU_BUF, MEMP_NUM_COAPPDUBUF, MEMP_LEN_COAPPDUBUF, "COAP_PDU_BUF") LWIP_MEMPOOL(COAP_LG_XMIT, MEMP_NUM_COAPLGXMIT, sizeof(coap_lg_xmit_t), "COAP_LG_XMIT") +#ifdef COAP_CLIENT_SUPPORT LWIP_MEMPOOL(COAP_LG_CRCV, MEMP_NUM_COAPLGCRCV, sizeof(coap_lg_crcv_t), "COAP_LG_CRCV") +#endif /* COAP_CLIENT_SUPPORT */ +#ifdef COAP_SERVER_SUPPORT LWIP_MEMPOOL(COAP_LG_SRCV, MEMP_NUM_COAPLGSRCV, sizeof(coap_lg_srcv_t), "COAP_LG_SRCV") - +LWIP_MEMPOOL(COAP_DIGEST_CTX, MEMP_NUM_COAPDIGESTCTX, sizeof(coap_digest_t) + sizeof(size_t), "COAP_DIGEST_CTX") +#endif /* COAP_SERVER_SUPPORT */ diff --git a/examples/lwip/server-coap.c b/examples/lwip/server-coap.c index 2ffa612d..dd21ba78 100644 --- a/examples/lwip/server-coap.c +++ b/examples/lwip/server-coap.c @@ -1,5 +1,5 @@ /* - * server-coap.h -- LwIP example + * server-coap.c -- LwIP example * * Copyright (C) 2013-2016 Christian Amsüss * Copyright (C) 2018-2022 Jon Shallow @@ -12,6 +12,7 @@ #include "coap_config.h" #include +#include "server-coap.h" coap_context_t *main_coap_context; @@ -111,7 +112,8 @@ init_coap_resources(coap_context_t *ctx) { coap_log(LOG_CRIT, "cannot create resource\n"); } -void server_coap_init(void) +void server_coap_init(coap_lwip_input_wait_handler_t input_wait, + void *input_arg, int log_level) { coap_address_t listenaddress; @@ -121,12 +123,21 @@ void server_coap_init(void) listenaddress.addr = *(IP_ANY_TYPE); listenaddress.port = COAP_DEFAULT_PORT; - coap_set_log_level(LOG_DEBUG); + coap_set_log_level(log_level); main_coap_context = coap_new_context(&listenaddress); - LWIP_ASSERT("Failed to initialize context", main_coap_context != NULL); + + /* Limit the number of idle sessions to save RAM (MEMP_NUM_COAPSESSION) */ + coap_context_set_max_idle_sessions(main_coap_context, MEMP_NUM_COAPSESSION); clock_offset = 1; /* Need a non-zero value */ init_coap_resources(main_coap_context); + coap_lwip_set_input_wait_handler(main_coap_context, input_wait, input_arg); +} + +void +server_coap_finished(void) { + coap_free_context(main_coap_context); + main_coap_context = NULL; } void server_coap_poll(void) @@ -135,6 +146,7 @@ void server_coap_poll(void) coap_tick_t ticks_now; coap_time_t time_now; + coap_io_process(main_coap_context, 1000); coap_ticks(&ticks_now); time_now = coap_ticks_to_rt(ticks_now); diff --git a/examples/lwip/server-coap.h b/examples/lwip/server-coap.h index e4597755..027559cf 100644 --- a/examples/lwip/server-coap.h +++ b/examples/lwip/server-coap.h @@ -12,6 +12,13 @@ #include "coap_config.h" #include -void server_coap_init(void); +/* Start up the CoAP Server */ +void server_coap_init(coap_lwip_input_wait_handler_t input_wait, void *input_arg, + coap_log_t log_level); + +/* Close down CoAP activity */ + +void server_coap_finished(void); + /* call this when you think that resources could be dirty */ void server_coap_poll(void); diff --git a/examples/lwip/server.c b/examples/lwip/server.c index bbfb1337..65648acf 100644 --- a/examples/lwip/server.c +++ b/examples/lwip/server.c @@ -46,46 +46,99 @@ static ip4_addr_t ipaddr, netmask, gw; +void +handle_sigint(int signum) { + (void)signum; + + server_coap_finished(); + printf("Server Application finished.\n"); + exit(0); +} + +static int +wait_for_input(void *arg, uint32_t milli_secs) { + struct netif *netif = (struct netif *)arg; + int ret; + + (void)milli_secs; + ret = tapif_select(netif); + + sys_check_timeouts(); + return ret; +} + int main(int argc, char **argv) { - struct netif netif; + struct netif netif; +#ifndef _WIN32 + struct sigaction sa; +#endif + int opt; + int log_level = 4; /* LOG_WARNING */ - /* startup defaults (may be overridden by one or more opts). this is - * hard-coded v4 even in presence of v6, which does auto-discovery and - * should thus wind up with an address of fe80::12:34ff:fe56:78ab%tap0 - * */ - IP4_ADDR(&gw, 192,168,113,1); - IP4_ADDR(&ipaddr, 192,168,113,2); - IP4_ADDR(&netmask, 255,255,255,0); + while ((opt = getopt(argc, argv, "v:")) != -1) { + switch (opt) { + case 'v': + log_level = atoi(optarg); + break; + default: + exit(1); + } + } - lwip_init(); + /* startup defaults (may be overridden by one or more opts). this is + * hard-coded v4 even in presence of v6, which does auto-discovery and + * should thus wind up with an address of fe80::12:34ff:fe56:78ab%tap0 + * */ + IP4_ADDR(&gw, 192,168,113,1); + IP4_ADDR(&ipaddr, 192,168,113,2); + IP4_ADDR(&netmask, 255,255,255,0); - printf("TCP/IP initialized.\n"); + lwip_init(); - netif_add(&netif, &ipaddr, &netmask, &gw, NULL, tapif_init, ethernet_input); - netif.flags |= NETIF_FLAG_ETHARP; - netif_set_default(&netif); - netif_set_up(&netif); + printf("TCP/IP initialized.\n"); + + netif_add(&netif, &ipaddr, &netmask, &gw, NULL, tapif_init, ethernet_input); + netif.flags |= NETIF_FLAG_ETHARP; + netif_set_default(&netif); + netif_set_up(&netif); #if LWIP_IPV6 - netif_create_ip6_linklocal_address(&netif, 1); + netif_create_ip6_linklocal_address(&netif, 1); #endif - /* start applications here */ + /* start applications here */ - server_coap_init(); +#ifdef _WIN32 + signal(SIGINT, handle_sigint); +#else + memset (&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = handle_sigint; + sa.sa_flags = 0; + sigaction (SIGINT, &sa, NULL); + sigaction (SIGTERM, &sa, NULL); + /* So we do not exit on a SIGPIPE */ + sa.sa_handler = SIG_IGN; + sigaction (SIGPIPE, &sa, NULL); +#endif - printf("Applications started.\n"); + server_coap_init(wait_for_input, &netif, log_level); + printf("Server Application started.\n"); - while (1) { - /* poll netif, pass packet to lwIP */ - tapif_select(&netif); + while (1) { + /* + * Poll netif, pass any read packet to lwIP + * Has internal timeout of 100 msec (sometimes less) based on + * sys_timeouts_sleeptime(). + */ + tapif_select(&netif); - sys_check_timeouts(); + sys_check_timeouts(); - server_coap_poll(); - } + server_coap_poll(); + } - return 0; + return 0; } diff --git a/include/coap3/coap_io_internal.h b/include/coap3/coap_io_internal.h index 75382bfb..6872ae7a 100644 --- a/include/coap3/coap_io_internal.h +++ b/include/coap3/coap_io_internal.h @@ -38,9 +38,11 @@ struct coap_socket_t { gnrc_pktsnip_t *pkt; /* pointer to received packet for processing */ #endif /* RIOT_VERSION */ coap_socket_flags_t flags; - coap_session_t *session; /* Used by the epoll logic for an active session. */ + coap_session_t *session; /* Used to determine session owner. */ +#if COAP_SERVER_SUPPORT coap_endpoint_t *endpoint; /* Used by the epoll logic for a listening endpoint. */ +#endif /* COAP_SERVER_SUPPORT */ }; /** @@ -156,12 +158,14 @@ void coap_packet_get_memmapped(coap_packet_t *packet, * the pbuf. */ struct pbuf *coap_packet_extract_pbuf(coap_packet_t *packet); + +void coap_io_process_timeout(void *arg); #endif #if defined(WITH_LWIP) /* * This is only included in coap_io.h instead of .c in order to be available for - * sizeof in lwippools.h. + * sizeof in config/lwippools.h. * Simple carry-over of the incoming pbuf that is later turned into a node. * * Source address data is currently side-banded via ip_current_dest_addr & co diff --git a/include/coap3/coap_net_internal.h b/include/coap3/coap_net_internal.h index 08e16eb9..c4391705 100644 --- a/include/coap3/coap_net_internal.h +++ b/include/coap3/coap_net_internal.h @@ -85,6 +85,8 @@ struct coap_context_t { #endif /* WITH_CONTIKI */ #ifdef WITH_LWIP + coap_lwip_input_wait_handler_t input_wait; /** Input wait / timeout handler if set */ + void *input_arg; /** argument to pass it input handler */ uint8_t timer_configured; /**< Set to 1 when a retransmission is * scheduled using lwIP timers for this * context, otherwise 0. */ diff --git a/include/coap3/libcoap.h b/include/coap3/libcoap.h index db585425..624f9221 100644 --- a/include/coap3/libcoap.h +++ b/include/coap3/libcoap.h @@ -30,10 +30,10 @@ #include typedef SSIZE_T ssize_t; typedef USHORT in_port_t; -#elif !defined (CONTIKI) +#elif !defined (CONTIKI) && !defined (WITH_LWIP) #include #include -#endif /* CONTIKI */ +#endif /* ! CONTIKI && ! WITH_LWIP */ #ifndef COAP_STATIC_INLINE # if defined(__cplusplus) diff --git a/include/coap3/mem.h b/include/coap3/mem.h index cf47d614..18a75ed6 100644 --- a/include/coap3/mem.h +++ b/include/coap3/mem.h @@ -45,9 +45,7 @@ typedef enum { COAP_PDU_BUF, COAP_RESOURCE, COAP_RESOURCEATTR, -#ifdef HAVE_LIBTINYDTLS COAP_DTLS_SESSION, -#endif COAP_SESSION, COAP_OPTLIST, COAP_CACHE_KEY, @@ -55,6 +53,7 @@ typedef enum { COAP_LG_XMIT, COAP_LG_CRCV, COAP_LG_SRCV, + COAP_DIGEST_CTX, } coap_memory_tag_t; #ifndef WITH_LWIP diff --git a/include/coap3/net.h b/include/coap3/net.h index 89a0cfe5..d4a3f07f 100644 --- a/include/coap3/net.h +++ b/include/coap3/net.h @@ -34,6 +34,7 @@ #include "coap_event.h" #include "pdu.h" #include "coap_session.h" +#include "coap_debug.h" /** * @ingroup application_api @@ -749,6 +750,52 @@ void coap_io_do_epoll(coap_context_t *ctx, struct epoll_event* events, /**@}*/ +/** + * @ingroup application_api + * @defgroup lwip LwIP specific API + * API for LwIP interface + * @{ + */ + +/** + * Dump the current state of the LwIP memory pools. + * + * Requires both MEMP_STATS and LWIP_STATS_DISPLAY to be defined as 1 + * in lwipopts.h + * + * @param log_level The logging level to use. + * + */ +void coap_lwip_dump_memory_pools(coap_log_t log_level); + +/** + * LwIP callback handler that can be used to wait / timeout for the + * next input packet. + * + * @param arg The argument passed to the coap_lwip_set_input_wait_handler() + * function. + * @param milli_secs Suggested number of milli secs to wait before returning + * if no input. + * + * @return @c 1 if packet received, @c 0 for timeout, else @c -1 on error. + */ +typedef int (*coap_lwip_input_wait_handler_t)(void* arg, uint32_t milli_secs); + +/** + * Set up a wait / timeout callback handler for use when + * the application calls coap_io_process(). + * + * @param context The coap context to associate this handler with. + * @param handler The handler to call while waiting for input. + * @param input_arg The argument to pass into handler(). + * + */ +void coap_lwip_set_input_wait_handler(coap_context_t *context, + coap_lwip_input_wait_handler_t handler, + void *input_arg); + +/**@}*/ + /** * @deprecated Use coap_io_process() instead. * diff --git a/include/coap3/uri.h b/include/coap3/uri.h index 02136afb..d97de6f0 100644 --- a/include/coap3/uri.h +++ b/include/coap3/uri.h @@ -47,7 +47,8 @@ typedef struct { uint16_t port; /**< The port in host byte order */ coap_str_const_t path; /**< Beginning of the first path segment. Use coap_split_path() to create Uri-Path options */ - coap_str_const_t query; /**< The query part if present */ + coap_str_const_t query; /**< The query part if present + Use coap_split_query() to create Uri-Query options */ /** The parsed scheme specifier. */ enum coap_uri_scheme_t scheme; diff --git a/libcoap-3.map b/libcoap-3.map index 5d3dc00d..a99d1fa1 100644 --- a/libcoap-3.map +++ b/libcoap-3.map @@ -105,6 +105,8 @@ global: coap_is_mcast; coap_join_mcast_group_intf; coap_log_impl; + coap_lwip_dump_memory_pools; + coap_lwip_set_input_wait_handler; coap_make_str_const; coap_malloc_type; coap_mcast_per_resource; diff --git a/libcoap-3.sym b/libcoap-3.sym index b56071e0..e8607f83 100644 --- a/libcoap-3.sym +++ b/libcoap-3.sym @@ -103,6 +103,8 @@ coap_io_process_with_fds coap_is_mcast coap_join_mcast_group_intf coap_log_impl +coap_lwip_dump_memory_pools +coap_lwip_set_input_wait_handler coap_make_str_const coap_malloc_type coap_mcast_per_resource diff --git a/man/Makefile.am b/man/Makefile.am index 4a04b588..ce8aeb14 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -30,6 +30,7 @@ TXT3 = coap_async.txt \ coap_io.txt \ coap_keepalive.txt \ coap_logging.txt \ + coap_lwip.txt \ coap_observe.txt \ coap_pdu_access.txt \ coap_pdu_setup.txt \ diff --git a/man/coap-client.txt.in b/man/coap-client.txt.in index 28f0baaa..a031b854 100644 --- a/man/coap-client.txt.in +++ b/man/coap-client.txt.in @@ -240,9 +240,9 @@ EXAMPLES -------- * Example ---- -coap-client coap://coap.me +coap-client coap://libcoap.net ---- -Query the resource '/' from server 'coap.me' (using the GET method). +Query the resource '/' from server 'libcoap.net' (using the GET method). * Example ---- diff --git a/man/coap_lwip.txt.in b/man/coap_lwip.txt.in new file mode 100644 index 00000000..f08a8231 --- /dev/null +++ b/man/coap_lwip.txt.in @@ -0,0 +1,108 @@ +// -*- mode:doc; -*- +// vim: set syntax=asciidoc,tw=0: + +coap_lwip(3) +============ +:doctype: manpage +:man source: coap_lwip +:man version: @PACKAGE_VERSION@ +:man manual: libcoap Manual + +NAME +---- +coap_lwip, +coap_coap_lwip_dump_memory_pools, +coap_lwip_set_input_wait_handler +- Work with CoAP lwip specific API handler + +SYNOPSIS +-------- +*#include * + +*void coap_lwip_set_input_wait_handler(coap_context_t *_context_, +coap_lwip_input_wait_handler_t _handler_, void *_input_arg_);* + +*void coap_lwip_dump_memory_pools(coap_log_t _log_level_);* + +DESCRIPTION +----------- +This man page describes the additional libcoap functions that are available +for working with LwIP implementations. + +*NOTE:* If the following line is defined in lwipopts.h, then libcoap will +reserve space for one response PDU when allocating coap_pdu_t structures. + +[source, c] +---- +#define MEMP_STATS 1 +---- + +CALLBACK HANDLER +---------------- + +*Callback Type: coap_lwip_input_wait_handler_t* + +[source, c] +---- +/** + * LwIP callback handler that can be used to wait / timeout for the + * next input packet. + * + * @param arg The argument passed to the coap_lwip_set_input_wait_handler() + * function. + * @param milli_secs Suggested number of milli secs to wait before returning + * if no input. + * + * @return @c 1 if packet received, @c 0 for timeout, else @c -1 on error. + */ +typedef int (*coap_lwip_input_wait_handler_t)(void* arg, uint32_t milli_secs); +---- + +FUNCTIONS +--------- + +*Function: coap_lwip_set_input_wait_handler()* + + +The *coap_lwip_set_input_wait_handler*() function is used to define a callback +_handler_ that is invoked by *coap_io_process*(3) which passes in the +_input_arg_ parameter along with a suggested milli-sec wait time parameter +in case there is no input. This callback _handler_ is used to wait for +(and probably times out) the next input packet. This allows the application +to define whatever mechanism it wants use for this process. + +*Function: coap_lwip_dump_memory_pools()* + +The *coap_lwip_dump_memory_pools*() function is used to dump out the current +state of the LwIP memory pools at logging level _log_level_. + +This function is always invoked by *coap_free_context*(3) with a _log_level_ of +LOG_DEBUG. + +*NOTE:* For information to be printed out, you need the following two lines in +lwipopts.h + +[source, c] +---- +#define MEMP_STATS 1 +#define LWIP_STATS_DISPLAY 1 +---- + +SEE ALSO +-------- +*coap_context*(3) and *coap_io*(3) + +FURTHER INFORMATION +------------------- +See "RFC7252: The Constrained Application Protocol (CoAP)" for further +information. + +BUGS +---- +Please report bugs on the mailing list for libcoap: +libcoap-developers@lists.sourceforge.net or raise an issue on GitHub at +https://github.com/obgm/libcoap/issues + +AUTHORS +------- +The libcoap project diff --git a/scripts/github_dist.sh b/scripts/github_dist.sh index 4a82c0ad..fe2ad76f 100755 --- a/scripts/github_dist.sh +++ b/scripts/github_dist.sh @@ -19,6 +19,14 @@ if test $err = 0 -a "x$ARCHIVE" != "x"; then $DIR/configure $PREFIX --enable-tests --enable-silent-rules --enable-documentation --enable-examples --disable-dtls && \ make EXTRA_CFLAGS=-Werror && make install EXTRA_CFLAGS=-Werror err=$? + if [ $err = 0 ] ; then + make -C $DIR/examples/lwip + err=$? + fi + if [ $err = 0 ] ; then + make -C $DIR/examples/contiki + err=$? + fi fi exit $err diff --git a/src/block.c b/src/block.c index f13c97e2..37756346 100644 --- a/src/block.c +++ b/src/block.c @@ -2385,16 +2385,9 @@ fail_resp: coap_lg_crcv_t *lg_crcv = coap_block_new_lg_crcv(session, sent); if (lg_crcv) { - uint8_t buf[8]; - size_t length = coap_encode_var_safe8(buf, - sizeof(lg_crcv->state_token), - lg_crcv->state_token); - if (coap_update_token(rcvd, length, buf)) { - LL_PREPEND(session->lg_crcv, lg_crcv); - return coap_handle_response_get_block(context, session, sent, rcvd, + LL_PREPEND(session->lg_crcv, lg_crcv); + return coap_handle_response_get_block(context, session, sent, rcvd, COAP_RECURSE_NO); - } - coap_block_delete_lg_crcv(session, lg_crcv); } } track_echo(session, rcvd); diff --git a/src/coap_io.c b/src/coap_io.c index 8db5613a..185e8f34 100644 --- a/src/coap_io.c +++ b/src/coap_io.c @@ -59,7 +59,7 @@ # include "uip.h" #endif -#if !defined(WITH_CONTIKI) && !defined(RIOT_VERSION) +#if !defined(WITH_CONTIKI) && !defined(RIOT_VERSION) && !(WITH_LWIP) /* define generic PKTINFO for IPv4 */ #if defined(IP_PKTINFO) # define GEN_IP_PKTINFO IP_PKTINFO @@ -145,6 +145,8 @@ void coap_socket_close(coap_socket_t *sock) { #else +#ifndef WITH_LWIP + #if COAP_SERVER_SUPPORT coap_endpoint_t * coap_malloc_endpoint(void) { @@ -575,8 +577,10 @@ coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) { return r; } -#endif /* WITH_CONTIKI */ +#endif /* ! WITH_LWIP */ +#endif /* ! WITH_CONTIKI */ +#if !defined(WITH_LWIP) #if (!defined(WITH_CONTIKI)) != ( defined(HAVE_NETINET_IN_H) || defined(HAVE_WS2TCPIP_H) ) /* define struct in6_pktinfo and struct in_pktinfo if not available FIXME: check with configure @@ -592,6 +596,7 @@ struct in_pktinfo { struct in_addr ipi_addr; }; #endif +#endif /* ! WITH_LWIP */ #if !defined(WITH_CONTIKI) && !defined(SOL_IP) /* Solaris expects level IPPROTO_IP for ancillary data. */ @@ -625,7 +630,7 @@ static __declspec(thread) LPFN_WSARECVMSG lpWSARecvMsg = NULL; #define ipi_spec_dst ipi_addr #endif -#ifndef RIOT_VERSION +#if !defined(RIOT_VERSION) && !defined(WITH_LWIP) ssize_t coap_network_send(coap_socket_t *sock, const coap_session_t *session, const uint8_t *data, size_t datalen) { ssize_t bytes_written = 0; @@ -796,17 +801,19 @@ coap_network_send(coap_socket_t *sock, const coap_session_t *session, const uint return bytes_written; } -#endif /* RIOT_VERSION */ +#endif /* ! RIOT_VERSION && ! WITH_LWIP */ #define SIN6(A) ((struct sockaddr_in6 *)(A)) +#ifndef WITH_LWIP void coap_packet_get_memmapped(coap_packet_t *packet, unsigned char **address, size_t *length) { *address = packet->payload; *length = packet->length; } +#endif /* ! WITH_LWIP */ -#ifndef RIOT_VERSION +#if !defined(RIOT_VERSION) && !defined(WITH_LWIP) ssize_t coap_network_read(coap_socket_t *sock, coap_packet_t *packet) { ssize_t len = -1; @@ -1123,11 +1130,10 @@ coap_io_prepare_epoll(coap_context_t *ctx, coap_tick_t now) { */ unsigned int coap_io_prepare_io(coap_context_t *ctx, - coap_socket_t *sockets[], - unsigned int max_sockets, - unsigned int *num_sockets, - coap_tick_t now) -{ + coap_socket_t *sockets[], + unsigned int max_sockets, + unsigned int *num_sockets, + coap_tick_t now) { coap_queue_t *nextpdu; coap_session_t *s, *rtmp; coap_tick_t timeout = 0; @@ -1135,10 +1141,10 @@ coap_io_prepare_io(coap_context_t *ctx, #if COAP_SERVER_SUPPORT int check_dtls_timeouts = 0; #endif /* COAP_SERVER_SUPPORT */ -#ifdef COAP_EPOLL_SUPPORT +#if defined(COAP_EPOLL_SUPPORT) || defined(WITH_LWIP) (void)sockets; (void)max_sockets; -#endif /* COAP_EPOLL_SUPPORT */ +#endif /* COAP_EPOLL_SUPPORT || WITH_LWIP */ *num_sockets = 0; @@ -1191,12 +1197,12 @@ coap_io_prepare_io(coap_context_t *ctx, session_timeout = COAP_DEFAULT_SESSION_TIMEOUT * COAP_TICKS_PER_SECOND; LL_FOREACH(ctx->endpoint, ep) { -#ifndef COAP_EPOLL_SUPPORT +#if !defined(COAP_EPOLL_SUPPORT) && !defined(WITH_LWIP) if (ep->sock.flags & (COAP_SOCKET_WANT_READ | COAP_SOCKET_WANT_WRITE | COAP_SOCKET_WANT_ACCEPT)) { if (*num_sockets < max_sockets) sockets[(*num_sockets)++] = &ep->sock; } -#endif /* ! COAP_EPOLL_SUPPORT */ +#endif /* ! COAP_EPOLL_SUPPORT i && ! WITH_LWIP */ SESSIONS_ITER_SAFE(ep->sessions, s, rtmp) { /* Check whether any idle server sessions should be released */ if (s->type == COAP_SESSION_TYPE_SERVER && s->ref == 0 && @@ -1248,12 +1254,12 @@ coap_io_prepare_io(coap_context_t *ctx, timeout = s_timeout; } } -#ifndef COAP_EPOLL_SUPPORT +#if !defined(COAP_EPOLL_SUPPORT) && !defined(WITH_LWIP) if (s->sock.flags & (COAP_SOCKET_WANT_READ|COAP_SOCKET_WANT_WRITE)) { if (*num_sockets < max_sockets) sockets[(*num_sockets)++] = &s->sock; } -#endif /* ! COAP_EPOLL_SUPPORT */ +#endif /* ! COAP_EPOLL_SUPPORT && ! WITH_LWIP */ release_1: coap_session_release(s); } @@ -1338,7 +1344,7 @@ release_1: } } -#ifndef COAP_EPOLL_SUPPORT +#if !defined(COAP_EPOLL_SUPPORT) && !defined(WITHLWIP) assert(s->ref > 1); if (s->sock.flags & (COAP_SOCKET_WANT_READ | COAP_SOCKET_WANT_WRITE | @@ -1346,7 +1352,7 @@ release_1: if (*num_sockets < max_sockets) sockets[(*num_sockets)++] = &s->sock; } -#endif /* ! COAP_EPOLL_SUPPORT */ +#endif /* ! COAP_EPOLL_SUPPORT && ! WITH_LWIP */ release_2: coap_session_release(s); } @@ -1355,7 +1361,7 @@ release_2: return (unsigned int)((timeout * 1000 + COAP_TICKS_PER_SECOND - 1) / COAP_TICKS_PER_SECOND); } -#ifndef RIOT_VERSION +#if !defined(RIOT_VERSION) && !defined(WITH_LWIP) int coap_io_process(coap_context_t *ctx, uint32_t timeout_ms) { return coap_io_process_with_fds(ctx, timeout_ms, 0, NULL, NULL, NULL); @@ -1560,7 +1566,7 @@ coap_io_process_with_fds(coap_context_t *ctx, uint32_t timeout_ms, return (int)(((now - before) * 1000) / COAP_TICKS_PER_SECOND); } -#endif /* RIOT_VERSION */ +#endif /* ! RIOT_VERSION && ! WITH_LWIP */ #else /* WITH_CONTIKI */ int coap_io_process(coap_context_t *ctx, uint32_t timeout_ms) { @@ -1606,10 +1612,12 @@ const char *coap_socket_strerror(void) { } #endif /* _WIN32 */ +#if !defined(WITH_LWIP) ssize_t coap_socket_send(coap_socket_t *sock, coap_session_t *session, const uint8_t *data, size_t data_len) { return session->context->network_send(sock, session, data, data_len); } +#endif /* ! WITH_LWIP */ #undef SIN6 diff --git a/src/coap_io_lwip.c b/src/coap_io_lwip.c index 5226d662..3f101a55 100644 --- a/src/coap_io_lwip.c +++ b/src/coap_io_lwip.c @@ -1,7 +1,7 @@ -/* coap_io_lwip.c -- Network I/O functions for libcoap on lwIP - * +/* * Copyright (C) 2012,2014 Olaf Bergmann * 2014 chrysn + * 2022 Jon Shallow * * SPDX-License-Identifier: BSD-2-Clause * @@ -16,12 +16,98 @@ #include "coap3/coap_internal.h" #include +#include -#if NO_SYS -pthread_mutex_t lwprot_mutex = PTHREAD_MUTEX_INITIALIZER; -pthread_t lwprot_thread = (pthread_t)0xDEAD; -int lwprot_count = 0; -#endif +void +coap_lwip_dump_memory_pools(coap_log_t log_level) { +#if MEMP_STATS && LWIP_STATS_DISPLAY + int i; + + /* Save time if not needed */ + if (log_level > coap_get_log_level()) + return; + + coap_log(log_level, "* LwIP custom memory pools information\n"); + /* + * Make sure LwIP and libcoap have been built with the same + * -DCOAP_CLIENT_ONLY or -DCOAP_SERVER_ONLY options for + * MEMP_MAX to be correct. + */ + for (i = 0; i < MEMP_MAX; i++) { + coap_log(log_level, "* %-17s avail %3d in-use %3d peak %3d failed %3d\n", + memp_pools[i]->stats->name, memp_pools[i]->stats->avail, + memp_pools[i]->stats->used, memp_pools[i]->stats->max, + memp_pools[i]->stats->err); + } +#endif /* MEMP_STATS && LWIP_STATS_DISPLAY */ +} + +void +coap_lwip_set_input_wait_handler(coap_context_t *context, + coap_lwip_input_wait_handler_t handler, + void *input_arg) { + context->input_wait = handler; + context->input_arg = input_arg; +} + +void +coap_io_process_timeout(void *arg) { + coap_context_t *context = (coap_context_t*)arg; + coap_tick_t before; + unsigned int num_sockets; + unsigned int timeout; + + coap_ticks(&before); + timeout = coap_io_prepare_io(context, NULL, 0, &num_sockets, before); + if (context->timer_configured) { + sys_untimeout(coap_io_process_timeout, (void*)context); + context->timer_configured = 0; + } + if (timeout == 0) { + /* Garbage collect 1 sec hence */ + timeout = 1000; + } +#ifdef COAP_DEBUG_WAKEUP_TIMES + coap_log(LOG_INFO, "****** Next wakeup msecs %u (1)\n", + timeout); +#endif /* COAP_DEBUG_WAKEUP_TIMES */ + sys_timeout(timeout, coap_io_process_timeout, context); + context->timer_configured = 1; +} + +int +coap_io_process(coap_context_t *context, uint32_t timeout_ms) { + coap_tick_t before; + coap_tick_t now; + unsigned int num_sockets; + unsigned int timeout; + + coap_ticks(&before); + timeout = coap_io_prepare_io(context, NULL, 0, &num_sockets, before); + if (timeout_ms != 0 && timeout_ms != COAP_IO_NO_WAIT && + timeout > timeout_ms) { + timeout = timeout_ms; + } + if (context->timer_configured) { + sys_untimeout(coap_io_process_timeout, (void*)context); + context->timer_configured = 0; + } + if (timeout == 0) { + /* Garbage collect 1 sec hence */ + timeout = 1000; + } +#ifdef COAP_DEBUG_WAKEUP_TIMES + coap_log(LOG_INFO, "****** Next wakeup msecs %u (2)\n", + timeout); +#endif /* COAP_DEBUG_WAKEUP_TIMES */ + if (context->input_wait) { + context->input_wait(context->input_arg, timeout); + } + context->timer_configured = 1; + sys_check_timeouts(); + coap_ticks(&now); + return (int)(((now - before) * 1000) / COAP_TICKS_PER_SECOND); +} #if 0 void coap_packet_copy_source(coap_packet_t *packet, coap_address_t *target) @@ -50,8 +136,8 @@ struct pbuf *coap_packet_extract_pbuf(coap_packet_t *packet) return ret; } - -/** Callback from lwIP when a package was received. +#if COAP_CLIENT_SUPPORT +/** Callback from lwIP when a package was received for a client. * * The current implementation deals this to coap_dispatch immediately, but * other mechanisms (as storing the package in a queue and later fetching it @@ -59,11 +145,80 @@ struct pbuf *coap_packet_extract_pbuf(coap_packet_t *packet) * * It handles everything coap_io_do_io does on other implementations. */ -static void coap_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) -{ +static void +coap_recvc(void *arg, struct udp_pcb *upcb, struct pbuf *p, + const ip_addr_t *addr, u16_t port) { + coap_pdu_t *pdu = NULL; + coap_session_t *session = (coap_session_t*)arg; + coap_packet_t *packet; + + assert(session); + + if (p->len < 4) { + /* Minimum size of CoAP header - ignore runt */ + return; + } + + packet = coap_malloc_type(COAP_PACKET, sizeof(coap_packet_t)); + + /* this is fatal because due to the short life of the packet, never should there be more than one coap_packet_t required */ + LWIP_ASSERT("Insufficient coap_packet_t resources.", packet != NULL); + packet->pbuf = p; + /* Need to do this as there may be holes in addr_info */ + memset(&packet->addr_info, 0, sizeof(packet->addr_info)); + packet->addr_info.remote.port = port; + packet->addr_info.remote.addr = *addr; + packet->addr_info.local.port = upcb->local_port; + packet->addr_info.local.addr = *ip_current_dest_addr(); + packet->ifindex = netif_get_index(ip_current_netif()); + + pdu = coap_pdu_from_pbuf(p); + if (!pdu) + goto error; + + if (!coap_pdu_parse(session->proto, p->payload, p->len, pdu)) { + goto error; + } + + LWIP_ASSERT("Proto not supported for LWIP", COAP_PROTO_NOT_RELIABLE(session->proto)); + coap_dispatch(session->context, session, pdu); + + coap_delete_pdu(pdu); + packet->pbuf = NULL; + coap_free_packet(packet); + return; + +error: + /* + * https://tools.ietf.org/html/rfc7252#section-4.2 MUST send RST + * https://tools.ietf.org/html/rfc7252#section-4.3 MAY send RST + */ + if (session) + coap_send_rst(session, pdu); + coap_delete_pdu(pdu); + if (packet) { + packet->pbuf = NULL; + coap_free_packet(packet); + } + return; +} +#endif /* ! COAP_CLIENT_SUPPORT */ + +#if COAP_SERVER_SUPPORT +/** Callback from lwIP when a package was received for a server. + * + * The current implementation deals this to coap_dispatch immediately, but + * other mechanisms (as storing the package in a queue and later fetching it + * when coap_io_do_io is called) can be envisioned. + * + * It handles everything coap_io_do_io does on other implementations. + */ +static void +coap_recvs(void *arg, struct udp_pcb *upcb, struct pbuf *p, + const ip_addr_t *addr, u16_t port) { coap_endpoint_t *ep = (coap_endpoint_t*)arg; coap_pdu_t *pdu = NULL; - coap_session_t *session; + coap_session_t *session = NULL; coap_tick_t now; coap_packet_t *packet; @@ -94,7 +249,15 @@ static void coap_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_ } coap_ticks(&now); - session = coap_endpoint_get_session(ep, packet, now); + if ((upcb->local_port == COAP_DEFAULT_PORT) || + (upcb->local_port == COAPS_DEFAULT_PORT)) { + /* packet for local server */ + session = coap_endpoint_get_session(ep, packet, now); + } else { + session = coap_session_get_by_peer(ep->context, &packet->addr_info.remote, + packet->ifindex); + } + if (!session) goto error; LWIP_ASSERT("Proto not supported for LWIP", COAP_PROTO_NOT_RELIABLE(session->proto)); @@ -133,7 +296,7 @@ coap_new_endpoint(coap_context_t *context, const coap_address_t *addr, coap_prot result->sock.pcb = udp_new_ip_type(IPADDR_TYPE_ANY); if (result->sock.pcb == NULL) goto error; - udp_recv(result->sock.pcb, coap_recv, (void*)result); + udp_recv(result->sock.pcb, coap_recvs, (void*)result); err = udp_bind(result->sock.pcb, &addr->addr, addr->port); if (err) { udp_remove(result->sock.pcb); @@ -156,17 +319,28 @@ void coap_free_endpoint(coap_endpoint_t *ep) udp_remove(ep->sock.pcb); coap_free_type(COAP_ENDPOINT, ep); } +#endif /* ! COAP_SERVER_SUPPORT */ ssize_t coap_socket_send_pdu(coap_socket_t *sock, coap_session_t *session, - coap_pdu_t *pdu) { + coap_pdu_t *pdu) { /* FIXME: we can't check this here with the existing infrastructure, but we * should actually check that the pdu is not held by anyone but us. the * respective pbuf is already exclusively owned by the pdu. */ + struct pbuf *pbuf; pbuf_realloc(pdu->pbuf, pdu->used_size + coap_pdu_parse_header_size(session->proto, pdu->pbuf->payload)); - udp_sendto(sock->pcb, pdu->pbuf, &session->addr_info.remote.addr, - session->addr_info.remote.port); + + if (coap_debug_send_packet()) { + /* Need to take a copy as we may be re-using the origin in a retransmit */ + pbuf = pbuf_clone(PBUF_TRANSPORT, PBUF_RAM, pdu->pbuf); + if (pbuf == NULL) + return -1; + udp_sendto(sock->pcb, pbuf, &session->addr_info.remote.addr, + session->addr_info.remote.port); + + pbuf_free(pbuf); + } return pdu->used_size; } @@ -184,15 +358,44 @@ coap_socket_bind_udp(coap_socket_t *sock, return 0; } +#if COAP_CLIENT_SUPPORT 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) { - return 0; + const coap_address_t *local_if, + const coap_address_t *server, + int default_port, + coap_address_t *local_addr, + coap_address_t *remote_addr) { + err_t err; + struct udp_pcb *pcb; + + pcb = udp_new(); + + if (!pcb) { + return 0; + } + + err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); + if (err) { + LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, + ("coap_socket_connect_udp: port bind failed\n")); + return 0; + } + + sock->session->addr_info.local.port = pcb->local_port; + + err = udp_connect(pcb, &server->addr, server->port); + if (err) { + return 0; + } + + sock->pcb = pcb; + + udp_recv(sock->pcb, coap_recvc, (void*)sock->session); + + return 1; } +#endif /* ! COAP_CLIENT_SUPPORT */ int coap_socket_connect_tcp1(coap_socket_t *sock, @@ -237,6 +440,9 @@ coap_socket_read(coap_socket_t *sock, uint8_t *data, size_t data_len) { } void coap_socket_close(coap_socket_t *sock) { + if (sock->pcb){ + udp_remove(sock->pcb); + } + sock->pcb = NULL; return; } - diff --git a/src/coap_notls.c b/src/coap_notls.c index ef20ae35..0af47ba0 100644 --- a/src/coap_notls.c +++ b/src/coap_notls.c @@ -219,7 +219,7 @@ typedef struct coap_local_hash_t { coap_digest_ctx_t * coap_digest_setup(void) { - coap_key_t *digest_ctx = coap_malloc(sizeof(coap_local_hash_t)); + coap_key_t *digest_ctx = coap_malloc_type(COAP_DIGEST_CTX, sizeof(coap_local_hash_t)); if (digest_ctx) { memset(digest_ctx, 0, sizeof(coap_local_hash_t)); @@ -230,7 +230,7 @@ coap_digest_setup(void) { void coap_digest_free(coap_digest_ctx_t *digest_ctx) { - coap_free(digest_ctx); + coap_free_type(COAP_DIGEST_CTX, digest_ctx); } int diff --git a/src/coap_session.c b/src/coap_session.c index f251d710..f5949b1a 100644 --- a/src/coap_session.c +++ b/src/coap_session.c @@ -428,7 +428,7 @@ ssize_t coap_session_write(coap_session_t *session, const uint8_t *data, size_t coap_log(LOG_DEBUG, "* %s: sent %zd bytes\n", coap_session_str(session), bytes_written); } else if (bytes_written < 0) { - coap_log(LOG_DEBUG, "* %s: failed to send %zd bytes\n", + coap_log(LOG_DEBUG, "* %s: failed to send %zd bytes\n", coap_session_str(session), datalen ); } return bytes_written; @@ -566,7 +566,18 @@ void coap_session_connected(coap_session_t *session) { coap_log(LOG_DEBUG, "** %s: mid=0x%x: transmitted after delay\n", coap_session_str(session), (int)q->pdu->mid); +#ifdef WITH_LWIP + coap_socket_t *sock = &session->sock; +#if COAP_SERVER_SUPPORT + if (sock->flags == COAP_SOCKET_EMPTY) { + assert(session->endpoint != NULL); + sock = &session->endpoint->sock; + } +#endif /* COAP_SERVER_SUPPORT */ + bytes_written = coap_socket_send_pdu(sock, session, q->pdu); +#else /* WITH_LWIP */ bytes_written = coap_session_send_pdu(session, q->pdu); +#endif /* WITH_LWIP */ if (q->pdu->type == COAP_MESSAGE_CON && COAP_PROTO_NOT_RELIABLE(session->proto)) { if (coap_wait_ack(session->context, session, q) >= 0) q = NULL; @@ -956,6 +967,7 @@ coap_session_create_client( goto error; coap_session_reference(session); + session->sock.session = session; if (proto == COAP_PROTO_UDP || proto == COAP_PROTO_DTLS) { coap_session_t *s, *rtmp; @@ -1116,6 +1128,7 @@ coap_session_t *coap_new_client_session( coap_log(LOG_DEBUG, "***%s: session %p: created outgoing session\n", coap_session_str(session), (void *)session); session = coap_session_connect(session); + session->context = ctx; } return session; } @@ -1639,8 +1652,8 @@ coap_free_endpoint(coap_endpoint_t *ep) { coap_session_t * coap_session_get_by_peer(const coap_context_t *ctx, - const coap_address_t *remote_addr, - int ifindex) { + const coap_address_t *remote_addr, + int ifindex) { coap_session_t *s, *rtmp; #if COAP_CLIENT_SUPPORT SESSIONS_ITER(ctx->sessions, s, rtmp) { @@ -1663,6 +1676,9 @@ coap_session_get_by_peer(const coap_context_t *ctx, return NULL; } +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 46 +#endif const char *coap_session_str(const coap_session_t *session) { static char szSession[2 * (INET6_ADDRSTRLEN + 8) + 24]; char *p = szSession, *end = szSession + sizeof(szSession); diff --git a/src/net.c b/src/net.c index f3c7c3a9..62bfe2d5 100644 --- a/src/net.c +++ b/src/net.c @@ -116,9 +116,6 @@ coap_free_node(coap_queue_t *node) { #include -static void coap_retransmittimer_execute(void *arg); -static void coap_retransmittimer_restart(coap_context_t *ctx); - COAP_STATIC_INLINE coap_queue_t * coap_malloc_node() { return (coap_queue_t *)memp_malloc(MEMP_COAP_NODE); @@ -621,8 +618,11 @@ coap_free_context(coap_context_t *context) { #ifdef WITH_LWIP context->sendqueue = NULL; - coap_retransmittimer_restart(context); -#endif + if (context->timer_configured) { + sys_untimeout(coap_io_process_timeout, (void*)context); + context->timer_configured = 0; + } +#endif /* WITH_LWIP */ #ifndef WITHOUT_ASYNC coap_delete_all_async(context); @@ -682,6 +682,9 @@ coap_free_context(coap_context_t *context) { memset(&the_coap_context, 0, sizeof(coap_context_t)); initialized = 0; #endif /* WITH_CONTIKI */ +#ifdef WITH_LWIP + coap_lwip_dump_memory_pools(LOG_DEBUG); +#endif /* WITH_LWIP */ } int @@ -806,10 +809,18 @@ coap_send_pdu(coap_session_t *session, coap_pdu_t *pdu, coap_queue_t *node) { #ifdef WITH_LWIP coap_socket_t *sock = &session->sock; +#if COAP_SERVER_SUPPORT if (sock->flags == COAP_SOCKET_EMPTY) { assert(session->endpoint != NULL); sock = &session->endpoint->sock; } +#endif /* COAP_SERVER_SUPPORT */ + + if (session->state != COAP_SESSION_STATE_ESTABLISHED || + (pdu->type == COAP_MESSAGE_CON && + session->con_active >= COAP_NSTART(session))) { + return coap_session_delay_pdu(session, pdu, node); + } bytes_written = coap_socket_send_pdu(sock, session, pdu); if (bytes_written >= 0 && pdu->type == COAP_MESSAGE_CON && @@ -1005,11 +1016,6 @@ coap_wait_ack(coap_context_t *context, coap_session_t *session, coap_insert_node(&context->sendqueue, node); -#ifdef WITH_LWIP - if (node == context->sendqueue) /* don't bother with timer stuff if there are earlier retransmits */ - coap_retransmittimer_restart(context); -#endif - #ifdef WITH_CONTIKI { /* (re-)initialize retransmission timer */ coap_queue_t *nextpdu; @@ -1044,68 +1050,45 @@ token_match(const uint8_t *a, size_t alen, int coap_client_delay_first(coap_session_t *session) { +#if COAP_CLIENT_SUPPORT if (session->type == COAP_SESSION_TYPE_CLIENT && session->doing_first) { - if (session->doing_first) { - int timeout_ms = 5000; -#ifdef WITH_LWIP -#include - struct netif *netif = ip_route(&session->sock.pcb->local_ip, - &session->addr_info.remote.addr); - if (!netif) { - session->doing_first = 0; - return 1; - } -#endif /* WITH_LWIP */ + int timeout_ms = 5000; - if (session->delay_recursive) { - assert(0); - return 1; - } else { - session->delay_recursive = 1; - } - /* - * Need to wait for first request to get out and response back before - * continuing.. Response handler has to clear doing_first if not an error. - */ - coap_session_reference(session); - while (session->doing_first != 0) { -#ifndef WITH_LWIP - int result = coap_io_process(session->context, 1000); -#else /* WITH_LWIP */ - int result; - coap_tick_t start; - coap_tick_t end; - - coap_ticks(&start); - result = tapif_select(netif); -#endif /* WITH_LWIP */ - - if (result < 0) { - session->doing_first = 0; - session->delay_recursive = 0; - coap_session_release(session); - return 0; - } -#ifdef WITH_LWIP - sys_check_timeouts(); - coap_ticks(&end); - result = (end - start) * 1000 / COAP_TICKS_PER_SECOND; -#endif /* WITH_LWIP */ - if (result <= timeout_ms) { - timeout_ms -= result; - } else { - if (session->doing_first == 1) { - /* Timeout failure of some sort with first request */ - coap_log(LOG_DEBUG, "** %s: timeout waiting for first response\n", - coap_session_str(session)); - session->doing_first = 0; - } - } - } - session->delay_recursive = 0; - coap_session_release(session); + if (session->delay_recursive) { + assert(0); + return 1; + } else { + session->delay_recursive = 1; } + /* + * Need to wait for first request to get out and response back before + * continuing.. Response handler has to clear doing_first if not an error. + */ + coap_session_reference(session); + while (session->doing_first != 0) { + int result = coap_io_process(session->context, 1000); + + if (result < 0) { + session->doing_first = 0; + session->delay_recursive = 0; + coap_session_release(session); + return 0; + } + if (result <= timeout_ms) { + timeout_ms -= result; + } else { + if (session->doing_first == 1) { + /* Timeout failure of some sort with first request */ + coap_log(LOG_DEBUG, "** %s: timeout waiting for first response\n", + coap_session_str(session)); + session->doing_first = 0; + } + } + } + session->delay_recursive = 0; + coap_session_release(session); } +#endif /* COAP_CLIENT_SUPPORT */ return 1; } @@ -1463,10 +1446,6 @@ coap_retransmit(coap_context_t *context, coap_queue_t *node) { node->t = (now - context->sendqueue_basetime) + (node->timeout << node->retransmit_cnt); } coap_insert_node(&context->sendqueue, node); -#ifdef WITH_LWIP - if (node == context->sendqueue) /* don't bother with timer stuff if there are earlier retransmits */ - coap_retransmittimer_restart(context); -#endif if (node->is_mcast) { coap_log(LOG_DEBUG, "** %s: mid=0x%x: mcast delayed transmission\n", @@ -1538,14 +1517,6 @@ coap_retransmit(coap_context_t *context, coap_queue_t *node) { return COAP_INVALID_MID; } -#ifdef WITH_LWIP -/* WITH_LWIP, this is handled by coap_recv in a different way */ -void -coap_io_do_io(coap_context_t *ctx, coap_tick_t now) { - return; -} -#else /* WITH_LWIP */ - static int coap_handle_dgram_for_proto(coap_context_t *ctx, coap_session_t *session, coap_packet_t *packet) { uint8_t *data; @@ -2072,7 +2043,6 @@ error: coap_delete_pdu(pdu); return -1; } -#endif /* not WITH_LWIP */ int coap_remove_from_queue(coap_queue_t **queue, coap_session_t *session, coap_mid_t id, coap_queue_t **node) { @@ -3822,81 +3792,3 @@ PROCESS_THREAD(coap_retransmit_process, ev, data) { /*---------------------------------------------------------------------------*/ #endif /* WITH_CONTIKI */ - -#ifdef WITH_LWIP -/* FIXME: retransmits that are not required any more due to incoming packages - * do *not* get cleared at the moment, the wakeup when the transmission is due - * is silently accepted. this is mainly due to the fact that the required - * checks are similar in two places in the code (when receiving ACK and RST) - * and that they cause more than one patch chunk, as it must be first checked - * whether the sendqueue item to be dropped is the next one pending, and later - * the restart function has to be called. nothing insurmountable, but it can - * also be implemented when things have stabilized, and the performance - * penality is minimal - * - * also, this completely ignores COAP_RESOURCE_CHECK_TIME. - * */ - -static void coap_retransmittimer_execute(void *arg) { - coap_context_t *ctx = (coap_context_t*)arg; - coap_tick_t now; - coap_tick_t elapsed; - coap_queue_t *nextinqueue; - - ctx->timer_configured = 0; - - coap_ticks(&now); - - elapsed = now - ctx->sendqueue_basetime; /* that's positive for sure, and unless we haven't been called for a complete wrapping cycle, did not wrap */ - - nextinqueue = coap_peek_next(ctx); - while (nextinqueue != NULL) { - if (nextinqueue->t > elapsed) { - nextinqueue->t -= elapsed; - break; - } else { - elapsed -= nextinqueue->t; - coap_retransmit(ctx, coap_pop_next(ctx)); - nextinqueue = coap_peek_next(ctx); - } - } - - ctx->sendqueue_basetime = now; - - coap_retransmittimer_restart(ctx); -} - -static void coap_retransmittimer_restart(coap_context_t *ctx) { - coap_tick_t now, elapsed, delay; - - if (ctx->timer_configured) { - printf("clearing\n"); - sys_untimeout(coap_retransmittimer_execute, (void*)ctx); - ctx->timer_configured = 0; - } - if (ctx->sendqueue != NULL) { - coap_ticks(&now); - elapsed = now - ctx->sendqueue_basetime; - if (ctx->sendqueue->t >= elapsed) { - delay = ctx->sendqueue->t - elapsed; - } else { - /* a strange situation, but not completely impossible. - * - * this happens, for example, right after - * coap_retransmittimer_execute, when a retransmission - * was *just not yet* due, and the clock ticked before - * our coap_ticks was called. - * - * not trying to retransmit anything now, as it might - * cause uncontrollable recursion; let's just try again - * with the next main loop run. - * */ - delay = 0; - } - - printf("scheduling for %d ticks\n", delay); - sys_timeout(delay, coap_retransmittimer_execute, (void*)ctx); - ctx->timer_configured = 1; - } -} -#endif diff --git a/src/pdu.c b/src/pdu.c index f730b5eb..b8f54d82 100644 --- a/src/pdu.c +++ b/src/pdu.c @@ -93,7 +93,7 @@ coap_pdu_from_pbuf( struct pbuf *pbuf ) return pdu; } -#endif +#endif /* LWIP */ coap_pdu_t * coap_pdu_init(coap_pdu_type_t type, coap_pdu_code_t code, coap_mid_t mid, @@ -104,6 +104,16 @@ coap_pdu_init(coap_pdu_type_t type, coap_pdu_code_t code, coap_mid_t mid, assert(code <= 0xff); assert(mid >= 0 && mid <= 0xffff); +#ifdef WITH_LWIP +#if MEMP_STATS + /* Reserve 1 PDU for a response packet */ + if (memp_pools[MEMP_COAP_PDU]->stats->used + 1 >= + memp_pools[MEMP_COAP_PDU]->stats->avail) { + memp_pools[MEMP_COAP_PDU]->stats->err++; + return NULL; + } +#endif /* MEMP_STATS */ +#endif /* LWIP */ pdu = coap_malloc_type(COAP_PDU, sizeof(coap_pdu_t)); if (!pdu) return NULL;