diff --git a/CMakeLists.txt b/CMakeLists.txt index 6139193b25..60ca9e4f40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,7 @@ include(tools/clang-tidy.cmake) ######## Define compiler flags ######## set(PRIVATE_COMPILE_DEFINITIONS "") set(PUBLIC_COMPILE_DEFINITIONS "") +set(PRIVATE_COMPILE_OPTIONS "") if(OC_DYNAMIC_ALLOCATION_ENABLED) list(APPEND PUBLIC_COMPILE_DEFINITIONS "OC_DYNAMIC_ALLOCATION") endif() @@ -148,6 +149,9 @@ if(OC_DNS_LOOKUP_IPV6_ENABLED) list(APPEND PRIVATE_COMPILE_DEFINITIONS "OC_DNS_LOOKUP_IPV6") endif() +######## Target dependencies ######## +set(TARGET_DEPENDENCIES "") + ######## Gather source files ######## file(GLOB COMMON_SRC ${PROJECT_SOURCE_DIR}/api/c-timestamp/timestamp_format.c @@ -176,8 +180,45 @@ if(UNIX) elseif(WIN32) file(GLOB PORT_SRC port/windows/*.c) set(PORT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/port/windows) +elseif(DEFINED ZEPHYR_BASE) + # Set the Zephyr CMake task zephyr_interface as dependency. + # Zephyr generates some platform headers in that CMake task which needs to be completed + # before iotivity-lite compilation can start or the headers can not be found. + list(APPEND TARGET_DEPENDENCIES zephyr_interface) + + # Set the Zephyr include directories globally. + # Taken from zephyr/cmake/extensions.cmake: zephyr_get_include_directories_for_lang(C includes) + get_property(zephyr_include_dirs TARGET zephyr_interface PROPERTY INTERFACE_INCLUDE_DIRECTORIES) + get_property(posix_subsys_include_dirs TARGET posix_subsys PROPERTY INTERFACE_INCLUDE_DIRECTORIES) + # The Zephyr IP subsystem provides helper functions for IPv6 multicast functions in the + # subsys/net/ip/ipv6.h header file. But this header file is not provided by any Zephyr CMake target. + set(net_ip_subsys_include_dirs ${ZEPHYR_BASE}/subsys/net/ip) + include_directories(${zephyr_include_dirs} ${posix_subsys_include_dirs} ${net_ip_subsys_include_dirs}) + + # Add Zephyr compile definitions. + # Taken from zephyr/cmake/extensions.cmake: zephyr_get_compile_definitions_for_lang(C definitions) + get_property(zephyr_compile_defs TARGET zephyr_interface PROPERTY INTERFACE_COMPILE_DEFINITIONS) + list(APPEND PRIVATE_COMPILE_DEFINITIONS ${zephyr_compile_defs}) + list(APPEND PRIVATE_COMPILE_DEFINITIONS CBOR_NO_FLOATING_POINT) + + # Add Zephyr compile options. + # Taken from zephyr/cmake/extensions.cmake: zephyr_get_compile_options_for_lang(C options) + get_property(zephyr_compile_opts TARGET zephyr_interface PROPERTY INTERFACE_COMPILE_OPTIONS) + list(APPEND PRIVATE_COMPILE_OPTIONS ${zephyr_compile_opts}) + + file(GLOB PORT_SRC port/zephyr/src/[a-r]*.c) + if(CONFIG_FILE_SYSTEM) + message(STATUS "Using file system storage.") + list(APPEND PORT_SRC port/zephyr/src/storage_fs.c) + elseif(CONFIG_FLASH) + message(STATUS "Using raw flash storage.") + list(APPEND PORT_SRC port/zephyr/src/storage_flash.c) + else() + message(FATAL_ERROR "Cannot determine the storage technology to use!") + endif() + set(PORT_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/port/zephyr/src) else() - message(ERROR "Can build only on Linux and Windows!") + message(FATAL_ERROR "Cannot determine the port to use!") endif() list(APPEND SERVER_SRC ${PORT_SRC}) list(APPEND CLIENT_SRC ${PORT_SRC}) @@ -237,32 +278,49 @@ oc_enable_clang_tidy() add_library(common-obj OBJECT ${COMMON_SRC}) target_compile_definitions(common-obj PRIVATE ${PRIVATE_COMPILE_DEFINITIONS} PUBLIC ${PUBLIC_COMPILE_DEFINITIONS}) +target_compile_options(common-obj PRIVATE ${PRIVATE_COMPILE_OPTIONS}) target_include_directories(common-obj PRIVATE ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/port ${PORT_INCLUDE_DIR}) +if(TARGET_DEPENDENCIES) + add_dependencies(common-obj ${TARGET_DEPENDENCIES}) +endif() add_library(client-obj OBJECT ${CLIENT_SRC}) target_compile_definitions(client-obj PRIVATE ${PRIVATE_COMPILE_DEFINITIONS} PUBLIC ${PUBLIC_COMPILE_DEFINITIONS} "OC_CLIENT") +target_compile_options(client-obj PRIVATE ${PRIVATE_COMPILE_OPTIONS}) target_include_directories(client-obj PRIVATE ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/port ${PORT_INCLUDE_DIR}) if(OC_SECURITY_ENABLED) target_include_directories(client-obj PRIVATE ${MBEDTLS_INCLUDE_DIRS}) endif() +if(TARGET_DEPENDENCIES) + add_dependencies(client-obj ${TARGET_DEPENDENCIES}) +endif() add_library(server-obj OBJECT ${SERVER_SRC}) target_compile_definitions(server-obj PRIVATE ${PRIVATE_COMPILE_DEFINITIONS} PUBLIC ${PUBLIC_COMPILE_DEFINITIONS} "OC_SERVER") +target_compile_options(server-obj PRIVATE ${PRIVATE_COMPILE_OPTIONS}) target_include_directories(server-obj PRIVATE ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/port ${PORT_INCLUDE_DIR}) if(OC_SECURITY_ENABLED) target_include_directories(server-obj PRIVATE ${MBEDTLS_INCLUDE_DIRS}) endif() +if(TARGET_DEPENDENCIES) + add_dependencies(server-obj ${TARGET_DEPENDENCIES}) +endif() add_library(client-server-obj OBJECT ${CLIENT_SRC}) target_compile_definitions(client-server-obj PRIVATE ${PRIVATE_COMPILE_DEFINITIONS} PUBLIC ${PUBLIC_COMPILE_DEFINITIONS} "OC_CLIENT" "OC_SERVER") +target_compile_options(client-server-obj PRIVATE ${PRIVATE_COMPILE_OPTIONS}) target_include_directories(client-server-obj PRIVATE ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/port ${PORT_INCLUDE_DIR}) if(OC_SECURITY_ENABLED) target_include_directories(client-server-obj PRIVATE ${MBEDTLS_INCLUDE_DIRS}) endif() +if(TARGET_DEPENDENCIES) + add_dependencies(client-server-obj ${TARGET_DEPENDENCIES}) +endif() if(OC_CLOUD_ENABLED) add_library(cloud-obj OBJECT ${CLOUD_SRC}) target_compile_definitions(cloud-obj PRIVATE ${PRIVATE_COMPILE_DEFINITIONS} PUBLIC ${PUBLIC_COMPILE_DEFINITIONS}) + target_compile_options(cloud-obj PRIVATE ${PRIVATE_COMPILE_OPTIONS}) target_include_directories(cloud-obj PRIVATE ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/port ${PORT_INCLUDE_DIR}) if(OC_SECURITY_ENABLED) target_include_directories(cloud-obj PRIVATE ${MBEDTLS_INCLUDE_DIRS}) @@ -271,6 +329,7 @@ endif() add_library(python-obj OBJECT ${PYTHON_SRC}) target_compile_definitions(python-obj PRIVATE ${PRIVATE_COMPILE_DEFINITIONS} OC_LIBRARY_EXPORT PUBLIC ${PUBLIC_COMPILE_DEFINITIONS} "OC_LIBRARY" "OC_CLIENT") +target_compile_options(python-obj PRIVATE ${PRIVATE_COMPILE_OPTIONS}) set_property(TARGET python-obj PROPERTY C_VISIBILITY_PRESET hidden) set_property(TARGET python-obj PROPERTY VISIBILITY_INLINES_HIDDEN ON) target_include_directories(python-obj PRIVATE ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/port ${PORT_INCLUDE_DIR}) @@ -309,7 +368,7 @@ set_target_properties(client-static PROPERTIES VERSION ${PROJECT_VERSION} ) -if(NOT MSVC) +if(TARGET_SUPPORTS_SHARED_LIBS AND NOT MSVC) # Since the library symbols are not explicitly exported, no proper DLL and import LIB are generated with MSVC add_library(client-shared SHARED ${client-lib-obj}) target_link_libraries(client-shared PRIVATE ${PRIVATE_LINK_LIBS}) @@ -365,7 +424,7 @@ set_target_properties(server-static PROPERTIES VERSION ${PROJECT_VERSION} ) -if(NOT MSVC) +if(TARGET_SUPPORTS_SHARED_LIBS AND NOT MSVC) # Since the library symbols are not explicitly exported, no proper DLL and import LIB are generated with MSVC add_library(server-shared SHARED ${server-lib-obj}) target_link_libraries(server-shared PRIVATE ${PRIVATE_LINK_LIBS}) @@ -426,7 +485,7 @@ set_target_properties(client-server-static PROPERTIES VERSION ${PROJECT_VERSION} ) -if(NOT MSVC) +if(TARGET_SUPPORTS_SHARED_LIBS AND NOT MSVC) # Since the library symbols are not explicitly exported, no proper DLL and import LIB are generated with MSVC add_library(client-server-shared SHARED ${client-server-lib-obj}) target_link_libraries(client-server-shared PRIVATE ${PRIVATE_LINK_LIBS}) @@ -596,7 +655,7 @@ set(INSTALL_TARGETS server-static server-shared client-server-static client-server-shared ) -if(MSVC) +if(NOT TARGET_SUPPORTS_SHARED_LIBS OR MSVC) # Since the library symbols are not explicitly exported, no proper DLL and import LIB are generated with MSVC set(INSTALL_TARGETS client-static diff --git a/api/oc_discovery.c b/api/oc_discovery.c index 66791b72ee..0156c5a7f9 100644 --- a/api/oc_discovery.c +++ b/api/oc_discovery.c @@ -37,6 +37,7 @@ #include "oc_endpoint.h" #ifdef OC_SECURITY +#include #include "security/oc_pstat.h" #include "security/oc_sdi.h" #include "security/oc_tls.h" diff --git a/api/oc_helpers.c b/api/oc_helpers.c index 930698ad2d..677444dcea 100644 --- a/api/oc_helpers.c +++ b/api/oc_helpers.c @@ -258,6 +258,7 @@ oc_join_string_array(oc_string_array_t *ocstringarray, oc_string_t *ocstring) strcpy((char *)oc_string(*ocstring) + len, ""); } +#ifdef OC_OSCORE int oc_conv_byte_array_to_hex_string(const uint8_t *array, size_t array_len, char *hex_str, size_t *hex_str_len) @@ -315,3 +316,4 @@ oc_conv_hex_string_to_byte_array(const char *hex_str, size_t hex_str_len, return 0; } +#endif /* OC_OSCORE */ diff --git a/deps/mbedtls.cmake b/deps/mbedtls.cmake index 49f8f31a2f..b1de6a70e8 100755 --- a/deps/mbedtls.cmake +++ b/deps/mbedtls.cmake @@ -13,9 +13,13 @@ file(GLOB MBEDTLS_SRC ) list(REMOVE_ITEM MBEDTLS_SRC ${PROJECT_SOURCE_DIR}/deps/mbedtls/library/certs.c - ${PROJECT_SOURCE_DIR}/deps/mbedtls/library/memory_buffer_alloc.c ${PROJECT_SOURCE_DIR}/deps/mbedtls/library/x509_crl.c ) +if(OC_DYNAMIC_ALLOCATION_ENABLED) + list(REMOVE_ITEM MBEDTLS_SRC + ${PROJECT_SOURCE_DIR}/deps/mbedtls/library/memory_buffer_alloc.c + ) +endif() add_library(mbedtls OBJECT ${MBEDTLS_SRC}) target_include_directories(mbedtls PRIVATE ${PROJECT_SOURCE_DIR} @@ -24,7 +28,11 @@ target_include_directories(mbedtls PRIVATE ${PROJECT_SOURCE_DIR}/deps/mbedtls/include ) target_compile_definitions(mbedtls PUBLIC ${PUBLIC_COMPILE_DEFINITIONS} PRIVATE ${PRIVATE_COMPILE_DEFINITIONS} __OC_RANDOM) +target_compile_options(mbedtls PRIVATE ${PRIVATE_COMPILE_OPTIONS}) # do not treat warnings as errors on Windows if(MSVC) target_compile_options(mbedtls PRIVATE /W1 /WX-) endif() +if(TARGET_DEPENDENCIES) + add_dependencies(mbedtls ${TARGET_DEPENDENCIES}) +endif() diff --git a/deps/tinycbor b/deps/tinycbor index 70aba6ba51..d393c16f3e 160000 --- a/deps/tinycbor +++ b/deps/tinycbor @@ -1 +1 @@ -Subproject commit 70aba6ba51881e5b8d108c105a17ed9cdee6bc30 +Subproject commit d393c16f3eb30d0c47e6f9d92db62272f0ec4dc7 diff --git a/deps/tinycbor.cmake b/deps/tinycbor.cmake index 6781a78440..9d12c3f41f 100755 --- a/deps/tinycbor.cmake +++ b/deps/tinycbor.cmake @@ -2,7 +2,9 @@ add_library(tinycbor-master OBJECT ${PROJECT_SOURCE_DIR}/deps/tinycbor/src/cborerrorstrings.c ${PROJECT_SOURCE_DIR}/deps/tinycbor/src/cborencoder.c ${PROJECT_SOURCE_DIR}/deps/tinycbor/src/cborencoder_close_container_checked.c + ${PROJECT_SOURCE_DIR}/deps/tinycbor/src/cborencoder_float.c ${PROJECT_SOURCE_DIR}/deps/tinycbor/src/cborparser.c + ${PROJECT_SOURCE_DIR}/deps/tinycbor/src/cborparser_float.c ${PROJECT_SOURCE_DIR}/deps/tinycbor/src/cborpretty.c ) @@ -10,7 +12,11 @@ target_include_directories(tinycbor-master PUBLIC ${PROJECT_SOURCE_DIR}/deps/tinycbor/src ) -target_compile_definitions(tinycbor-master PUBLIC ${PUBLIC_COMPILE_DEFINITIONS}) +target_compile_definitions(tinycbor-master PUBLIC ${PUBLIC_COMPILE_DEFINITIONS} PRIVATE ${PRIVATE_COMPILE_DEFINITIONS}) +target_compile_options(tinycbor-master PRIVATE ${PRIVATE_COMPILE_OPTIONS}) +if(TARGET_DEPENDENCIES) + add_dependencies(tinycbor-master ${TARGET_DEPENDENCIES}) +endif() install(DIRECTORY ${PROJECT_SOURCE_DIR}/deps/tinycbor/src/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/iotivity-lite/deps/tinycbor/src COMPONENT dev diff --git a/include/oc_helpers.h b/include/oc_helpers.h index dcb739a828..0ca70d3c14 100644 --- a/include/oc_helpers.h +++ b/include/oc_helpers.h @@ -279,6 +279,7 @@ void _oc_alloc_string_array( #endif oc_string_array_t *ocstringarray, size_t size); +#ifdef OC_OSCORE /** Conversions between hex encoded strings and byte arrays */ /** @@ -304,7 +305,7 @@ int oc_conv_byte_array_to_hex_string(const uint8_t *array, size_t array_len, */ int oc_conv_hex_string_to_byte_array(const char *hex_str, size_t hex_str_len, uint8_t *array, size_t *array_len); - +#endif /* OC_OSCORE */ #ifdef __cplusplus } #endif diff --git a/include/oc_introspection.h b/include/oc_introspection.h index 9a793e4012..9ccc369aa4 100644 --- a/include/oc_introspection.h +++ b/include/oc_introspection.h @@ -50,8 +50,6 @@ #ifndef OC_INTROSPECTION_H #define OC_INTROSPECTION_H -#include - #include #include diff --git a/port/zephyr/README.md b/port/zephyr/README.md new file mode 100644 index 0000000000..af5a655e9e --- /dev/null +++ b/port/zephyr/README.md @@ -0,0 +1,293 @@ +# Usage of Zephyr Port + +## Zephyr application setup + +The iotivity-lite port for Zephyr currently has to be built as +[Third-party Library Code](https://docs.zephyrproject.org/latest/develop/application/index.html#third-party-library-code) +of the [Zephyr application](https://docs.zephyrproject.org/latest/develop/application/index.html). + +Add the iotivity-lite git repository as a sub-directory (e.g. a git submodule) to the Zephyr application structure: + +``` +/app +├── iotivity-lite +├── CMakeLists.txt +├── prj.conf +└── src + └── main.c +``` + +The `app/CMakeLists.txt` needs to add the `iotivity-lite` as a subdirectory to cross-compile it to Zephyr +(the iotivity-lite CMake will detect that it has the zephyr port has to be used and also does some checks +on the Zephyr configuration, e.g. detect whether the zephyr file system is enabled an can be used for storage). + +If needed, the Zephyr application can configure the iotivity-lite build by pre-setting CMake variables that +iotivity-lite uses prior to the `add_subdirectory` command. + +Finally, the Zephyr application then links to one of the `*-static` CMake targets from iotivity-lite. + +For example: + +``` +cmake_minimum_required(VERSION 3.10) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) + +project(iotivity_lite_app) + +target_sources(app PRIVATE src/main.c) + +# Configure iotivity-lite build by pre-setting CMake variables +set(BUILD_EXAMPLE_APPLICATIONS OFF CACHE BOOL "Build example applications." FORCE) +set(OC_IDD_API_ENABLED OFF CACHE BOOL "Enable the Introspection Device Data API." FORCE) +set(OC_TCP_ENABLED OFF CACHE BOOL "Enable OCF communications over TCP. Necessary for Cloud communications." FORCE) +set(OC_IPV4_ENABLED ON CACHE BOOL "Enable IPv4 support." FORCE) +#set(OC_DEBUG_ENABLED ON CACHE BOOL "Enable debug messages." FORCE) +set(OC_SECURITY_ENABLED ON CACHE BOOL "Enable security." FORCE) + +# Build iotivity-lite sub-project +add_subdirectory(iotivity-lite) + +# Link iotivity-lite to application +target_link_libraries(app PUBLIC server-static) +``` + +## Zephyr project configuration + +The iotivity-lite build requires some Zephyr modules and project configuration to be present + +### Storage + +iotivity-lite can use either the LittleFS file-system module or the raw flash as storage. + +Note: During the port update only the LittleFS file-system has been tested. The raw flash storage +just has been taken from the previous port without modification and has not been tested. + +The following project configuration snippet sets up the LittleFS file-system module for the application: + +``` +CONFIG_FLASH=y +CONFIG_FLASH_SHELL=y +CONFIG_FLASH_MAP=y +CONFIG_FLASH_MAP_SHELL=y + +CONFIG_FILE_SYSTEM=y +CONFIG_FILE_SYSTEM_LITTLEFS=y +CONFIG_FILE_SYSTEM_SHELL=y +``` + +### Posix + +iotivity-lite requires the posix API with pthread: + +``` +CONFIG_POSIX_API=y +CONFIG_PTHREAD_IPC=y +# increase pthread count (default 5) +# Needed? +CONFIG_MAX_PTHREAD_COUNT=10 +``` + +### Networking + +Currently the networking should be initialized automatically by Zephyr with `CONFIG_NET_CONFIG_AUTO_INIT=y`. +This causes the networking stack to be initialized by Zephyr prior to calling the `main()` function of the +application. + +Also the protocols need to be enabled according to the iotivity-lite build configuration: `CONFIG_NET_UDP, CONFIG_NET_ARP, CONFIG_NET_IPV6, CONFIG_NET_IPV4, CONFIG_NET_IPV4_IGMP, CONFIG_NET_DHCPV4` + +It is required to increase the number of IPv6 multicast addresses to accommodate the iotivity-lite multicast addresses. + +The `ipadapter.c` implementation of the Zephyr port also uses the socket API and network management events, +which need to be enabled with `CONFIG_NET_SOCKETS`, `CONFIG_NET_MGMT`, and `CONFIG_NET_MGMT_EVENT`. + +On the [STM32H747I Discovery Board](https://docs.zephyrproject.org/latest/boards/arm/stm32h747i_disco/doc/index.html) +the port was tested with, the packet buffer sizes needed to be increased as well. + +``` +CONFIG_NETWORKING=y +CONFIG_NET_IPV6=y +CONFIG_NET_IPV4=y +CONFIG_NET_ARP=y +CONFIG_NET_UDP=y +CONFIG_NET_DHCPV4=y +CONFIG_NET_IPV4_IGMP=y + +# Initialize the networking system automatically +CONFIG_NET_CONFIG_SETTINGS=y +CONFIG_NET_CONFIG_AUTO_INIT=y +CONFIG_NET_CONFIG_NEED_IPV4=y + +# increase number of ipv6 multicast addresses (default 3). +# iotivity-lite adds 3 (6 is OC_WKCORE is is defined). +CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=5 + +# Increase network packet buffer counts +CONFIG_NET_PKT_RX_COUNT=400 +CONFIG_NET_PKT_TX_COUNT=400 +CONFIG_NET_BUF_RX_COUNT=600 +CONFIG_NET_BUF_TX_COUNT=600 + +CONFIG_NET_MGMT=y +CONFIG_NET_MGMT_EVENT=y + +CONFIG_NET_SOCKETS=y +# Needed? +CONFIG_NET_SOCKETS_POLL_MAX=20 +CONFIG_NET_SOCKETPAIR=y +``` + +## iotivity-lite initialization + +With the iotivity-lite library linked and the Zephyr configuration done, the Zephyr application can use the +iotivity-lite stack like on any other platform and drive the iotivity-lite event loop using Zephyr primitives. + +For example: + +``` +#include +LOG_MODULE_REGISTER(iotivity_lite_example, LOG_LEVEL_DBG); + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +static struct k_sem block; + +FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage); +static struct fs_mount_t lfs_storage_mnt = { + .type = FS_LITTLEFS, + .fs_data = &storage, + .storage_dev = (void *)FLASH_AREA_ID(storage), + .mnt_point = "/lfs", +}; + +static void +signal_event_loop(void) +{ + k_sem_give(&block); +} + +static void +register_resources(void) +{ + // Here custom resources can be created during initialization of iotivity-lite +} + +static void +set_device_custom_property(void *data) +{ + (void)data; + oc_set_custom_device_property(snr, "123456"); +} + +static int +app_init(void) +{ + int ret = oc_init_platform("Zephyr", NULL, NULL); + ret |= oc_add_device("/oic/d", + "x.com.zephyr.device", + "Zephyr Board", + "ocf.2.2.5", + "ocf.res.1.3.0,ocf.sh.1.3.0", + set_device_custom_property, NULL); + return ret; +} + +void main(void) +{ + int rc; + struct fs_mount_t *mp = &lfs_storage_mnt; + unsigned int id = (uintptr_t)mp->storage_dev; + const struct flash_area *pfa; + struct fs_statvfs sbuf; + + static const oc_handler_t handler = { + .init = app_init, + .signal_event_loop = signal_event_loop, + .register_resources = register_resources + }; + + k_sem_init(&block, 0, 1); + + LOG_INF("Mount LittleFS partition"); + rc = flash_area_open(id, &pfa); + if (rc < 0) { + LOG_ERR("FAIL: unable to find flash area %u: %d", id, rc); + return; + } + LOG_INF("Flash area %u at 0x%x on %s for %u bytes", + id, (unsigned int)pfa->fa_off, pfa->fa_dev_name, + (unsigned int)pfa->fa_size); + flash_area_close(pfa); + + rc = fs_mount(mp); + if (rc < 0) { + LOG_ERR("FAIL: mount id %" PRIuPTR " at %s: %d", + (uintptr_t)mp->storage_dev, mp->mnt_point, rc); + return; + } + LOG_INF("%s mount: %d", mp->mnt_point, rc); + + rc = fs_statvfs(mp->mnt_point, &sbuf); + if (rc < 0) { + LOG_ERR("FAIL: statvfs: %d", rc); + goto out; + } + + LOG_INF("%s: bsize = %lu; frsize = %lu; blocks = %lu ; bfree = %lu", + mp->mnt_point, + sbuf.f_bsize, sbuf.f_frsize, + sbuf.f_blocks, sbuf.f_bfree); + +#ifdef OC_STORAGE + oc_storage_config(lfs_storage_mnt.mnt_point); +#endif + if (oc_main_init(&handler) < 0) { + LOG_ERR("iotivity-lite initialization failed!"); + goto out; + } + + // Run iotivity-lite event loop + oc_clock_time_t next_event; + k_timeout_t k_tmo; + + while (true) { + next_event = oc_main_poll(); + if (next_event == 0) + next_event = K_FOREVER.ticks; + else + next_event -= oc_clock_time(); + k_tmo.ticks = next_event; + k_sem_take(&block, k_tmo); + } + + oc_main_shutdown(); +out: + rc = fs_unmount(mp); + LOG_INF("%s unmount: %d", mp->mnt_point, rc); +} +``` + +## Build Zephyr application + +With this setup, the Zephyr application including the iotivity-lite stack can be built using the +[Zephyr Toolchain](https://docs.zephyrproject.org/latest/develop/application/index.html#building-an-application). + +For example using `west`: +``` +cd /app +west build -b +``` \ No newline at end of file diff --git a/port/zephyr/src/clock.c b/port/zephyr/src/clock.c index edae98a912..d3565427a3 100644 --- a/port/zephyr/src/clock.c +++ b/port/zephyr/src/clock.c @@ -36,5 +36,7 @@ oc_clock_seconds(void) void oc_clock_wait(oc_clock_time_t t) { - k_sleep(t); + k_timeout_t kt; + kt.ticks = t; + k_sleep(kt); } diff --git a/port/zephyr/src/ipadapter.c b/port/zephyr/src/ipadapter.c index 57ea520dd2..f4a31a7ba7 100644 --- a/port/zephyr/src/ipadapter.c +++ b/port/zephyr/src/ipadapter.c @@ -1,5 +1,6 @@ /* // Copyright (c) 2016 Intel Corporation +// Copyright (c) 2022 Kistler Instruments // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,370 +14,1588 @@ // See the License for the specific language governing permissions and // limitations under the License. */ - +#include +LOG_MODULE_REGISTER(oc_ipadapter, LOG_LEVEL_DBG); +#define _GNU_SOURCE +#include "ipadapter.h" +#include "ipcontext.h" +#include "oc_config.h" +#ifdef OC_TCP +#include "tcpadapter.h" +#endif #include "oc_buffer.h" +#include "oc_core_res.h" #include "oc_endpoint.h" +#include "oc_network_monitor.h" +#include "port/oc_assert.h" #include "port/oc_connectivity.h" +#include "util/oc_atomic.h" +#include +#include #include +#include +#include +#include "ipv6.h" +#include +#include +#include #include -#include - -#include -#include -#include -#include - -/* Server's receive socket */ -static struct net_context *udp_recv6; - -/* "All OCF nodes" multicast address and port */ -#define OCF_MCAST_IP6ADDR \ - { \ - { \ - { \ - 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x58 \ - } \ - } \ - } -static struct in6_addr in6addr_mcast = OCF_MCAST_IP6ADDR; -#define OCF_MCAST_PORT (5683) -/* Multicast receive socket */ -static struct net_context *mcast_recv6; -static struct sockaddr_in6 mcast_addr6; -static struct sockaddr_in6 my_addr6; - -static struct in6_addr in6addr_my; +#include +#include +#include -#ifdef OC_SECURITY -/* DTLS receive socket */ -static struct net_context *dtls_recv6; -static struct sockaddr_in6 dtls_addr6; -#define MY_DTLS_PORT (56789) -#endif /* OC_SECURITY */ -static oc_endpoint_t *eps; -/* For synchronizing the network receive thread with IoTivity-Lite's - * event loop. +#define OCF_PORT_UNSECURED (5683) +static const uint8_t ALL_OCF_NODES_LL[] = { + 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x58 +}; +static const uint8_t ALL_OCF_NODES_RL[] = { + 0xff, 0x03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x58 +}; +static const uint8_t ALL_OCF_NODES_SL[] = { + 0xff, 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x58 +}; + +#ifdef OC_WKCORE +static const uint8_t ALL_COAP_NODES_LL[] = { 0xff, 0x02, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0xFD }; +static const uint8_t ALL_COAP_NODES_RL[] = { 0xff, 0x03, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0xFD }; +static const uint8_t ALL_COAP_NODES_SL[] = { 0xff, 0x05, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0xFD }; +#endif + +#define ALL_COAP_NODES_V4 0xe00001bb + +#define NETWORK_THREAD_STACKSIZE (1024 + CONFIG_TEST_EXTRA_STACKSIZE) +static pthread_attr_t oc_network_thread_attr; +K_THREAD_STACK_DEFINE(oc_network_thread_stack, NETWORK_THREAD_STACKSIZE); +static pthread_mutex_t oc_network_mutex; +static struct net_mgmt_event_callback net_mgmt_event_if_cb; +bool ifchange_initialized; + +OC_LIST(ip_contexts); +OC_MEMB(ip_context_s, ip_context_t, OC_MAX_NUM_DEVICES); + +OC_MEMB(device_eps, oc_endpoint_t, 8 * OC_MAX_NUM_DEVICES); // fix + +#ifdef OC_NETWORK_MONITOR +/** + * Structure to manage interface list. */ -static struct k_sem sem; +typedef struct ip_interface +{ + struct ip_interface *next; + int if_index; +} ip_interface_t; + +OC_LIST(ip_interface_list); +OC_MEMB(ip_interface_s, ip_interface_t, OC_MAX_IP_INTERFACES); + +OC_LIST(oc_network_interface_cb_list); +OC_MEMB(oc_network_interface_cb_s, oc_network_interface_cb_t, + OC_MAX_NETWORK_INTERFACE_CBS); + +static ip_interface_t * +get_ip_interface(int target_index) +{ + ip_interface_t *if_item = oc_list_head(ip_interface_list); + while (if_item != NULL && if_item->if_index != target_index) { + if_item = if_item->next; + } + return if_item; +} + +static bool +add_ip_interface(int target_index) +{ + if (get_ip_interface(target_index)) + return false; + + ip_interface_t *new_if = oc_memb_alloc(&ip_interface_s); + if (!new_if) { + OC_ERR("Failed to allocate memory for ip interface list item"); + return false; + } + new_if->if_index = target_index; + oc_list_add(ip_interface_list, new_if); + OC_DBG("Added to ip interface list: %d", new_if->if_index); + return true; +} + +static void +check_new_ip_interfaces_cb(struct net_if *iface, void *user_data) +{ + add_ip_interface(net_if_get_by_iface(iface)); +} + +static bool +check_new_ip_interfaces(void) +{ + net_if_foreach(&check_new_ip_interfaces_cb, NULL); + return true; +} + +static bool +remove_ip_interface(int target_index) +{ + ip_interface_t *if_item = get_ip_interface(target_index); + if (!if_item) { + return false; + } + + oc_list_remove(ip_interface_list, if_item); + oc_memb_free(&ip_interface_s, if_item); + OC_DBG("Removed from ip interface list: %d", target_index); + return true; +} + +static void +remove_all_ip_interface(void) +{ + ip_interface_t *if_item = oc_list_head(ip_interface_list), *next; + while (if_item != NULL) { + next = if_item->next; + oc_list_remove(ip_interface_list, if_item); + oc_memb_free(&ip_interface_s, if_item); + if_item = next; + } +} + +static void +remove_all_network_interface_cbs(void) +{ + oc_network_interface_cb_t *cb_item = + oc_list_head(oc_network_interface_cb_list), + *next; + while (cb_item != NULL) { + next = cb_item->next; + oc_list_remove(oc_network_interface_cb_list, cb_item); + oc_memb_free(&oc_network_interface_cb_s, cb_item); + cb_item = next; + } +} +#endif /* OC_NETWORK_MONITOR */ + +#ifdef OC_SESSION_EVENTS +OC_LIST(oc_session_event_cb_list); +OC_MEMB(oc_session_event_cb_s, oc_session_event_cb_t, OC_MAX_SESSION_EVENT_CBS); + +static void +remove_all_session_event_cbs(void) +{ + oc_session_event_cb_t *cb_item = oc_list_head(oc_session_event_cb_list), + *next; + while (cb_item != NULL) { + next = cb_item->next; + oc_list_remove(oc_session_event_cb_list, cb_item); + oc_memb_free(&oc_session_event_cb_s, cb_item); + cb_item = next; + } +} + +#endif /* OC_SESSION_EVENTS */ void oc_network_event_handler_mutex_init(void) { - k_sem_init(&sem, 0, 1); - k_sem_give(&sem); + if (pthread_mutex_init(&oc_network_mutex, NULL) != 0) { + oc_abort("error initializing network event handler mutex"); + } } void oc_network_event_handler_mutex_lock(void) { - k_sem_take(&sem, K_FOREVER); + pthread_mutex_lock(&oc_network_mutex); } void oc_network_event_handler_mutex_unlock(void) { - k_sem_give(&sem); + pthread_mutex_unlock(&oc_network_mutex); } void oc_network_event_handler_mutex_destroy(void) { + ifchange_initialized = false; +#ifdef OC_NETWORK_MONITOR + remove_all_ip_interface(); + remove_all_network_interface_cbs(); +#endif /* OC_NETWORK_MONITOR */ +#ifdef OC_SESSION_EVENTS + remove_all_session_event_cbs(); +#endif /* OC_SESSION_EVENTS */ + pthread_mutex_destroy(&oc_network_mutex); +} + +static ip_context_t * +get_ip_context_for_device(size_t device) +{ + ip_context_t *dev = oc_list_head(ip_contexts); + while (dev != NULL && dev->device != device) { + dev = dev->next; + } + if (!dev) { + return NULL; + } + return dev; +} + +#ifdef OC_IPV4 +static int +net_if_manage_ipv4_mcast_group(struct net_if *iface) +{ + struct in_addr addr; + addr.s_addr = htonl(ALL_COAP_NODES_V4); + struct net_if_mcast_addr *mcast_addr = + net_if_ipv4_maddr_lookup(&addr, &iface); + if (mcast_addr == NULL) { + OC_DBG("Add IPv4 multicast address on interface with index %d", + net_if_get_by_iface(iface)); + mcast_addr = net_if_ipv4_maddr_add(iface, &addr); + if (mcast_addr == NULL) { + OC_ERR("Failed to add IPv4 multicast address"); + return -1; + } + } + if (!net_if_ipv4_maddr_is_joined(mcast_addr)) { + OC_DBG("Join IPv4 multicast address on interface with index %d", + net_if_get_by_iface(iface)); + net_if_ipv4_maddr_join(mcast_addr); + } + return 0; +} +#endif /* OC_IPV4 */ + +static int +net_if_manage_ipv6_mcast_group(struct net_if *iface) +{ + /* Link-local scope */ + struct in6_addr addr; + memcpy(addr.s6_addr, ALL_OCF_NODES_LL, 16); + // struct net_if_mcast_addr *mcast_addr = net_if_ipv6_maddr_lookup(&addr, + // &iface); if(mcast_addr == NULL) + // { + // OC_ERR("No link-local multicast address on interface with index %d", + // net_if_get_by_iface(iface)); return -1; + // } + // net_if_ipv6_maddr_join(mcast_addr); + int ret = net_ipv6_mld_join(iface, &addr); + if (ret == -EALREADY) { + return 0; + } else if (ret < 0) { + OC_ERR("Cannot join link-local IPv6 multicast group (%d)", ret); + return -1; + } + + /* Realm-local scope */ + memcpy(addr.s6_addr, ALL_OCF_NODES_RL, 16); + // mcast_addr = net_if_ipv6_maddr_lookup(&addr, &iface); + // if(mcast_addr == NULL) + // { + // OC_ERR("No realm-local multicast address on interface with index %d", + // net_if_get_by_iface(iface)); return -1; + // } + // net_if_ipv6_maddr_join(mcast_addr); + ret = net_ipv6_mld_join(iface, &addr); + if (ret == -EALREADY) { + return 0; + } else if (ret < 0) { + OC_ERR("Cannot join realm-local IPv6 multicast group (%d)", ret); + return -1; + } + + /* Site-local scope */ + memcpy(addr.s6_addr, ALL_OCF_NODES_SL, 16); + // mcast_addr = net_if_ipv6_maddr_lookup(&addr, &iface); + // if(mcast_addr == NULL) + // { + // OC_ERR("No site-local multicast address on interface with index %d", + // net_if_get_by_iface(iface)); return -1; + // } + // net_if_ipv6_maddr_join(mcast_addr); + ret = net_ipv6_mld_join(iface, &addr); + if (ret == -EALREADY) { + return 0; + } else if (ret < 0) { + OC_ERR("Cannot join site-local IPv6 multicast group (%d)", ret); + return -1; + } + return 0; } static void -oc_network_receive(struct net_context *context, struct net_pkt *pkt, int status, - void *user_data) -{ - oc_message_t *message = oc_allocate_message(); - - if (message) { - uint16_t pos; - struct net_udp_hdr *udp = - (struct net_udp_hdr *)((u8_t *)(NET_IPV6_HDR(pkt)) + - sizeof(struct net_ipv6_hdr)); - size_t bytes_read = net_pkt_appdatalen(pkt); - if (bytes_read < 0) { - oc_message_unref(message); +configure_mcast_socket_net_if(struct net_if *iface, void *user_data) +{ + int sa_family = *(int *)user_data; + /* Ignore interfaces that are down */ + if (!net_if_is_up(iface)) { + return; + } + /* Ignore interfaces not belonging to the address family under consideration + */ + if (sa_family == AF_INET6) { + net_if_manage_ipv6_mcast_group(iface); + } +#ifdef OC_IPV4 + else if (sa_family == AF_INET) { + net_if_manage_ipv4_mcast_group(iface); + } +#endif +} + +static int +configure_mcast_socket(int mcast_sock, int sa_family) +{ + net_if_foreach(&configure_mcast_socket_net_if, &sa_family); + return 0; +} + +struct get_interface_addresses_params +{ + ip_context_t *dev; + unsigned char family; + uint16_t port; + bool secure; + bool tcp; +}; + +static void +get_interface_addresses_net_if(struct net_if *iface, void *user_data) +{ + struct get_interface_addresses_params *params = user_data; + oc_endpoint_t ep = { 0 }; + ep.interface_index = net_if_get_by_iface(iface); + /* Ignore interfaces that are down */ + if (!net_if_is_up(iface)) { + OC_DBG("Interface %d is down", ep.interface_index); + return; + } + + if (params->secure) { + ep.flags |= SECURED; + } +#ifdef OC_IPV4 + if (params->family == AF_INET) { + ep.addr.ipv4.port = params->port; + } else +#endif /* OC_IPV4 */ + if (params->family == AF_INET6) { + ep.addr.ipv6.port = params->port; + } +#ifdef OC_TCP + if (params->tcp) { + ep.flags |= TCP; + } +#endif /* OC_TCP */ + if (params->family == AF_INET6) { + ep.flags |= IPV6; + struct net_if_ipv6 *ipv6; + if (net_if_config_ipv6_get(iface, &ipv6) < 0) { + OC_ERR("net_if_config_ipv6_get for interface %d failed", + ep.interface_index); return; } - - size_t offset_from_start = net_pkt_get_len(pkt) - bytes_read; - bytes_read = (bytes_read < OC_PDU_SIZE) ? bytes_read : OC_PDU_SIZE; - struct net_buf *frag = net_frag_read(pkt->frags, offset_from_start, &pos, - bytes_read, message->data); - if (!frag && pos == 0xffff) { - net_pkt_unref(pkt); - oc_message_unref(message); + for (int i = 0; i < NET_IF_MAX_IPV6_ADDR; i++) { + if (!ipv6->unicast[i].is_used || + ipv6->unicast[i].address.family != AF_INET6) { + continue; + } + if (net_ipv6_is_addr_unspecified(&(ipv6->unicast[i].address.in6_addr)) || + net_ipv6_is_addr_loopback(&(ipv6->unicast[i].address.in6_addr))) { + continue; + } + memcpy(ep.addr.ipv6.address, ipv6->unicast[i].address.in6_addr.s6_addr, + 16); + ep.addr.ipv6.scope = ipv6->unicast[i].address.in6_addr.s6_addr[1]; + oc_endpoint_t *new_ep = oc_memb_alloc(&device_eps); + if (!new_ep) { + return; + } + memcpy(new_ep, &ep, sizeof(oc_endpoint_t)); +#ifdef OC_DEBUG + char buf[IPADDR_BUFF_SIZE]; + SNPRINTFipaddr(buf, IPADDR_BUFF_SIZE, ep); + OC_DBG("add endpoint: %s, family: %d, if index: %d, secure: %d, tcp: %d", + buf, (int)params->family, ep.interface_index, (int)params->secure, + (int)params->tcp); +#endif /* OC_DEBUG */ + oc_list_add(params->dev->eps, new_ep); + } + } +#ifdef OC_IPV4 + if (params->family == AF_INET) { + ep.flags |= IPV4; + struct net_if_ipv4 *ipv4; + if (net_if_config_ipv4_get(iface, &ipv4) < 0) { + OC_ERR("net_if_config_ipv6_get for interface %d failed", + ep.interface_index); return; } + for (int i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { + if (!ipv4->unicast[i].is_used || + ipv4->unicast[i].address.family != AF_INET) { + continue; + } + if (net_ipv4_is_addr_unspecified(&(ipv4->unicast[i].address.in_addr)) || + net_ipv4_is_addr_loopback(&(ipv4->unicast[i].address.in_addr))) { + continue; + } + memcpy(ep.addr.ipv4.address, ipv4->unicast[i].address.in_addr.s4_addr, 4); + oc_endpoint_t *new_ep = oc_memb_alloc(&device_eps); + if (!new_ep) { + return; + } + memcpy(new_ep, &ep, sizeof(oc_endpoint_t)); +#ifdef OC_DEBUG + char buf[IPADDR_BUFF_SIZE]; + SNPRINTFipaddr(buf, IPADDR_BUFF_SIZE, ep); + OC_DBG("add endpoint: %s, family: %d, if index: %d, secure: %d, tcp: %d", + buf, (int)params->family, ep.interface_index, (int)params->secure, + (int)params->tcp); +#endif /* OC_DEBUG */ + oc_list_add(params->dev->eps, new_ep); + } + } +#endif /* OC_IPV4 */ +} + +static void +get_interface_addresses(ip_context_t *dev, unsigned char family, uint16_t port, + bool secure, bool tcp) +{ + struct get_interface_addresses_params params = { + .dev = dev, .family = family, .port = port, .secure = secure, .tcp = tcp + }; + net_if_foreach(&get_interface_addresses_net_if, ¶ms); +} + +static void +free_endpoints_list(ip_context_t *dev) +{ + oc_endpoint_t *ep = oc_list_pop(dev->eps); - message->length = bytes_read; - if (user_data != NULL) { - message->endpoint.flags = IPV6 | SECURED; + while (ep != NULL) { + oc_memb_free(&device_eps, ep); + ep = oc_list_pop(dev->eps); + } +} + +static void +refresh_endpoints_list(ip_context_t *dev) +{ + free_endpoints_list(dev); + + get_interface_addresses(dev, AF_INET6, dev->port, false, false); +#ifdef OC_SECURITY + get_interface_addresses(dev, AF_INET6, dev->dtls_port, true, false); +#endif /* OC_SECURITY */ +#ifdef OC_IPV4 + get_interface_addresses(dev, AF_INET, dev->port4, false, false); +#ifdef OC_SECURITY + get_interface_addresses(dev, AF_INET, dev->dtls4_port, true, false); +#endif /* OC_SECURITY */ +#endif /* OC_IPV4 */ + +#ifdef OC_TCP + get_interface_addresses(dev, AF_INET6, dev->tcp.port, false, true); +#ifdef OC_SECURITY + get_interface_addresses(dev, AF_INET6, dev->tcp.tls_port, true, true); +#endif /* OC_SECURITY */ +#ifdef OC_IPV4 + get_interface_addresses(dev, AF_INET, dev->tcp.port4, false, true); +#ifdef OC_SECURITY + get_interface_addresses(dev, AF_INET, dev->tcp.tls4_port, true, true); +#endif /* OC_SECURITY */ +#endif /* OC_IPV4 */ +#endif /* OC_TCP */ +} + +oc_endpoint_t * +oc_connectivity_get_endpoints(size_t device) +{ + ip_context_t *dev = get_ip_context_for_device(device); + + if (!dev) { + return NULL; + } + + bool refresh = false; + bool swapped = false; + int8_t expected = OC_ATOMIC_LOAD8(dev->flags); + while ((expected & IP_CONTEXT_FLAG_REFRESH_ENDPOINT_LIST) != 0) { + int8_t desired = expected & ~IP_CONTEXT_FLAG_REFRESH_ENDPOINT_LIST; + OC_ATOMIC_COMPARE_AND_SWAP8(dev->flags, expected, desired, swapped); + if (swapped) { + refresh = true; + break; + } + } + + if (refresh || oc_list_length(dev->eps) == 0) { + refresh_endpoints_list(dev); + } + + return oc_list_head(dev->eps); +} + +/* Called after network interface up/down events. + * This function reconfigures IPv6/v4 multicast sockets for + * all logical devices. + */ +static void +process_interface_change_event(struct net_if *iface, oc_interface_event_t event) +{ + int num_devices = oc_core_get_num_devices(); + + if (event == NETWORK_INTERFACE_UP) { +#ifdef OC_NETWORK_MONITOR + if (add_ip_interface(net_if_get_by_iface(iface))) { + oc_network_interface_event(event); + } +#endif /* OC_NETWORK_MONITOR */ + } else if (event == NETWORK_INTERFACE_DOWN) { +#ifdef OC_NETWORK_MONITOR + if (remove_ip_interface(net_if_get_by_iface(iface))) { + oc_network_interface_event(event); + } +#endif /* OC_NETWORK_MONITOR */ + } + + for (int i = 0; i < num_devices; i++) { + ip_context_t *dev = get_ip_context_for_device(i); + oc_network_event_handler_mutex_lock(); + refresh_endpoints_list(dev); + oc_network_event_handler_mutex_unlock(); + } +} + +static int +recv_msg(int sock, uint8_t *recv_buf, int recv_buf_size, + oc_endpoint_t *endpoint, bool multicast) +{ + struct sockaddr server_sockaddr; + socklen_t server_sockaddr_len; + server_sockaddr_len = sizeof(server_sockaddr); + int ret = zsock_getsockname(sock, &server_sockaddr, &server_sockaddr_len); + if (ret < 0) { + OC_ERR("getsockname() failed: %d", errno); + return -1; + } + struct sockaddr client_sockaddr; + socklen_t client_sockaddr_len; + client_sockaddr_len = sizeof(client_sockaddr); + + ret = recvfrom(sock, recv_buf, recv_buf_size, 0, &client_sockaddr, + &client_sockaddr_len); + if (ret < 0) { + OC_ERR("recvfrom() failed: %d", errno); + return -1; + } + + if (client_sockaddr.sa_family == AF_INET6) { + /* Set source address of packet in endpoint structure */ + struct sockaddr_in6 *c6 = net_sin6(&client_sockaddr); + memcpy(endpoint->addr.ipv6.address, c6->sin6_addr.s6_addr, + sizeof(c6->sin6_addr.s6_addr)); + endpoint->addr.ipv6.scope = c6->sin6_scope_id; + endpoint->addr.ipv6.port = ntohs(c6->sin6_port); + /* Set receiving network interface index */ + struct net_if *iface; + struct net_if_addr *if_addr = + net_if_ipv6_addr_lookup(&net_sin6(&server_sockaddr)->sin6_addr, &iface); + endpoint->interface_index = net_if_get_by_iface(iface); + /* For a unicast receiving socket, extract the interface address + * into the endpoint's addr_local attribute. + * This would be used to set the source address of a response that + * results from this message. + */ + if (!multicast) { + memcpy(endpoint->addr_local.ipv6.address, + if_addr->address.in6_addr.s6_addr, 16); + } else { + memset(endpoint->addr_local.ipv6.address, 0, 16); + } + } +#ifdef OC_IPV4 + else if (client_sockaddr.sa_family == AF_INET) { + /* Set source address of packet in endpoint structure */ + struct sockaddr_in *c4 = net_sin(&client_sockaddr); + memcpy(endpoint->addr.ipv4.address, &c4->sin_addr.s_addr, + sizeof(c4->sin_addr.s_addr)); + endpoint->addr.ipv6.port = ntohs(c4->sin_port); + /* Set receiving network interface index */ + struct net_if *iface; + struct net_if_addr *if_addr = + net_if_ipv4_addr_lookup(&net_sin(&server_sockaddr)->sin_addr, &iface); + endpoint->interface_index = net_if_get_by_iface(iface); + /* For a unicast receiving socket, extract the interface address + * into the endpoint's addr_local attribute. + * This would be used to set the source address of a response that + * results from this message. + */ + if (!multicast) { + memcpy(endpoint->addr_local.ipv4.address, + &if_addr->address.in_addr.s_addr, 4); + } else { + memset(endpoint->addr_local.ipv4.address, 0, 4); + } + } +#endif /* OC_IPV4 */ + return ret; +} + +static void +oc_udp_add_socks_to_fd_set(ip_context_t *dev) +{ + FD_SET(dev->server_sock, &dev->rfds); + FD_SET(dev->mcast_sock, &dev->rfds); +#ifdef OC_SECURITY + FD_SET(dev->secure_sock, &dev->rfds); +#endif /* OC_SECURITY */ + +#ifdef OC_IPV4 + FD_SET(dev->server4_sock, &dev->rfds); + FD_SET(dev->mcast4_sock, &dev->rfds); #ifdef OC_SECURITY - message->encrypted = 1; + FD_SET(dev->secure4_sock, &dev->rfds); #endif /* OC_SECURITY */ - } else - message->endpoint.flags = IPV6; - memcpy(message->endpoint.addr.ipv6.address, &NET_IPV6_HDR(pkt)->src, 16); - message->endpoint.addr.ipv6.scope = 0; - message->endpoint.addr.ipv6.port = ntohs(udp->src_port); - message->endpoint.device = 0; - - OC_DBG("oc_network_receive: received %d bytes", (int)message->length); +#endif /* OC_IPV4 */ +} + +static adapter_receive_state_t +oc_udp_receive_message(ip_context_t *dev, fd_set *fds, oc_message_t *message) +{ + if (FD_ISSET(dev->server_sock, fds)) { + int count = recv_msg(dev->server_sock, message->data, OC_PDU_SIZE, + &message->endpoint, false); + if (count < 0) { + return ADAPTER_STATUS_ERROR; + } + message->length = (size_t)count; + message->endpoint.flags = IPV6; + FD_CLR(dev->server_sock, fds); + return ADAPTER_STATUS_RECEIVE; + } + + if (FD_ISSET(dev->mcast_sock, fds)) { + int count = recv_msg(dev->mcast_sock, message->data, OC_PDU_SIZE, + &message->endpoint, true); + if (count < 0) { + return ADAPTER_STATUS_ERROR; + } + message->length = (size_t)count; + message->endpoint.flags = IPV6 | MULTICAST; + FD_CLR(dev->mcast_sock, fds); + return ADAPTER_STATUS_RECEIVE; + } + +#ifdef OC_IPV4 + if (FD_ISSET(dev->server4_sock, fds)) { + int count = recv_msg(dev->server4_sock, message->data, OC_PDU_SIZE, + &message->endpoint, false); + if (count < 0) { + return ADAPTER_STATUS_ERROR; + } + message->length = (size_t)count; + message->endpoint.flags = IPV4; + FD_CLR(dev->server4_sock, fds); + return ADAPTER_STATUS_RECEIVE; + } + + if (FD_ISSET(dev->mcast4_sock, fds)) { + int count = recv_msg(dev->mcast4_sock, message->data, OC_PDU_SIZE, + &message->endpoint, true); + if (count < 0) { + return ADAPTER_STATUS_ERROR; + } + message->length = (size_t)count; + message->endpoint.flags = IPV4 | MULTICAST; + FD_CLR(dev->mcast4_sock, fds); + return ADAPTER_STATUS_RECEIVE; + } +#endif /* OC_IPV4 */ + +#ifdef OC_SECURITY + if (FD_ISSET(dev->secure_sock, fds)) { + int count = recv_msg(dev->secure_sock, message->data, OC_PDU_SIZE, + &message->endpoint, false); + if (count < 0) { + return ADAPTER_STATUS_ERROR; + } + message->length = (size_t)count; + message->endpoint.flags = IPV6 | SECURED; + message->encrypted = 1; + FD_CLR(dev->secure_sock, fds); + return ADAPTER_STATUS_RECEIVE; + } +#ifdef OC_IPV4 + if (FD_ISSET(dev->secure4_sock, fds)) { + int count = recv_msg(dev->secure4_sock, message->data, OC_PDU_SIZE, + &message->endpoint, false); + if (count < 0) { + return ADAPTER_STATUS_ERROR; + } + message->length = (size_t)count; + message->endpoint.flags = IPV4 | SECURED; + message->encrypted = 1; + FD_CLR(dev->secure4_sock, fds); + return ADAPTER_STATUS_RECEIVE; + } +#endif /* OC_IPV4 */ +#endif /* OC_SECURITY */ + + return ADAPTER_STATUS_NONE; +} + +static void * +network_event_thread(void *data) +{ + ip_context_t *dev = (ip_context_t *)data; + + fd_set setfds; + FD_ZERO(&dev->rfds); + FD_SET(dev->shutdown_pipe[0], &dev->rfds); + oc_udp_add_socks_to_fd_set(dev); +#ifdef OC_TCP + oc_tcp_add_socks_to_fd_set(dev); +#endif /* OC_TCP */ + + int i, n; + while (OC_ATOMIC_LOAD8(dev->terminate) != 1) { + setfds = ip_context_rfds_fd_copy(dev); + n = select(FD_SETSIZE, &setfds, NULL, NULL, NULL); + + if (FD_ISSET(dev->shutdown_pipe[0], &setfds)) { + char buf; + // write to pipe shall not block - so read the byte we wrote + if (read(dev->shutdown_pipe[0], &buf, 1) < 0) { + // intentionally left blank + } + } + + if (OC_ATOMIC_LOAD8(dev->terminate)) { + break; + } + + for (i = 0; i < n; i++) { + oc_message_t *message = oc_allocate_message(); + if (!message) { + OC_WRN("Failed to allocate message!"); + break; + } + + message->endpoint.device = dev->device; + + if (oc_udp_receive_message(dev, &setfds, message) == + ADAPTER_STATUS_RECEIVE) { + goto common; + } +#ifdef OC_TCP + if (oc_tcp_receive_message(dev, &setfds, message) == + ADAPTER_STATUS_RECEIVE) { + goto common; + } +#endif /* OC_TCP */ + + oc_message_unref(message); + continue; + + common: #ifdef OC_DEBUG - PRINT("oc_network_receive: incoming message from "); - PRINTipaddr(message->endpoint); - PRINT("\n\n"); + PRINT("Incoming message of size %zd bytes from ", message->length); + PRINTipaddr(message->endpoint); + PRINT("\n"); #endif /* OC_DEBUG */ - oc_network_event(message); + oc_network_event(message); + } } - - net_pkt_unref(pkt); + pthread_exit(NULL); + return NULL; } -static inline void -udp_sent(struct net_context *context, int status, void *token, void *user_data) +static int +send_msg(int sock, struct sockaddr *receiver, oc_message_t *message) { - if (!status) { - OC_DBG("oc_send_buffer: sent %d bytes", POINTER_TO_UINT(token)); - } else if (status < 0) { - OC_DBG("oc_send_buffer: failed: (%d)", status); + int bytes_sent = 0, x; + uint8_t *buf = message->data; + size_t remaining = message->length; + while (bytes_sent < (int)message->length) { + buf += bytes_sent; + remaining -= bytes_sent; + x = sendto(sock, buf, remaining, 0, receiver, sizeof(struct sockaddr)); + if (x < 0) { + OC_WRN("sendto() returned errno %d", errno); + break; + } + bytes_sent += x; } + OC_DBG("Sent %d bytes", bytes_sent); + + if (bytes_sent == 0) { + return -1; + } + + return bytes_sent; } int oc_send_buffer(oc_message_t *message) { #ifdef OC_DEBUG - PRINT("oc_send_buffer: outgoing message to "); + PRINT("Outgoing message of size %zd bytes to ", message->length); PRINTipaddr(message->endpoint); - PRINT("\n\n"); + PRINT("\n"); #endif /* OC_DEBUG */ - /* Populate destination address structure */ - struct sockaddr_in6 peer_addr; - memcpy(peer_addr.sin6_addr.in6_u.u6_addr8, - message->endpoint.addr.ipv6.address, 16); - peer_addr.sin6_family = AF_INET6; - peer_addr.sin6_port = htons(message->endpoint.addr.ipv6.port); + struct sockaddr receiver; + memset(&receiver, 0, sizeof(struct sockaddr)); +#ifdef OC_IPV4 + if (message->endpoint.flags & IPV4) { + struct sockaddr_in *r = (struct sockaddr_in *)&receiver; + memcpy(&r->sin_addr.s_addr, message->endpoint.addr.ipv4.address, + sizeof(r->sin_addr.s_addr)); + r->sin_family = AF_INET; + r->sin_port = htons(message->endpoint.addr.ipv4.port); + } else { +#else + { +#endif + struct sockaddr_in6 *r = net_sin6(&receiver); + memcpy(r->sin6_addr.s6_addr, message->endpoint.addr.ipv6.address, + sizeof(r->sin6_addr.s6_addr)); + r->sin6_family = AF_INET6; + r->sin6_port = htons(message->endpoint.addr.ipv6.port); + r->sin6_scope_id = message->endpoint.addr.ipv6.scope; + } + int send_sock = -1; + + ip_context_t *dev = get_ip_context_for_device(message->endpoint.device); + + if (!dev) { + return -1; + } + +#ifdef OC_TCP + if (message->endpoint.flags & TCP) { + return oc_tcp_send_buffer(dev, message, &receiver); + } +#endif /* OC_TCP */ - /* Network buffer to hold data to be sent */ - struct net_pkt *send_pkt; #ifdef OC_SECURITY if (message->endpoint.flags & SECURED) { - send_pkt = net_pkt_get_tx(dtls_recv6, K_NO_WAIT); +#ifdef OC_IPV4 + if (message->endpoint.flags & IPV4) { + send_sock = dev->secure4_sock; + } else { + send_sock = dev->secure_sock; + } +#else /* OC_IPV4 */ + send_sock = dev->secure_sock; +#endif /* !OC_IPV4 */ } else #endif /* OC_SECURITY */ +#ifdef OC_IPV4 + if (message->endpoint.flags & IPV4) { + send_sock = dev->server4_sock; + } else { + send_sock = dev->server_sock; + } +#else /* OC_IPV4 */ { - send_pkt = net_pkt_get_tx(udp_recv6, K_NO_WAIT); + send_sock = dev->server_sock; } - if (!send_pkt) { - OC_WRN("oc_send_buffer: cannot acquire send_pkt"); +#endif /* !OC_IPV4 */ + + return send_msg(send_sock, &receiver, message); +} + +#ifdef OC_CLIENT +void +oc_send_discovery_request(oc_message_t *message) +{ + // TODO: implement sending of multicast message + OC_ERR("Not implemented"); +} +#endif /* OC_CLIENT */ + +#ifdef OC_NETWORK_MONITOR +int +oc_add_network_interface_event_callback(interface_event_handler_t cb) +{ + if (!cb) + return -1; + + oc_network_interface_cb_t *cb_item = + oc_memb_alloc(&oc_network_interface_cb_s); + if (!cb_item) { + OC_ERR("Failed to allocate memory for network interface callback"); return -1; } - bool status = - net_pkt_append_all(send_pkt, message->length, message->data, K_NO_WAIT); - if (!status) { - OC_WRN("oc_send_buffer: cannot populate send_pkt"); + cb_item->handler = cb; + oc_list_add(oc_network_interface_cb_list, cb_item); + return 0; +} + +int +oc_remove_network_interface_event_callback(interface_event_handler_t cb) +{ + if (!cb) + return -1; + + oc_network_interface_cb_t *cb_item = + oc_list_head(oc_network_interface_cb_list); + while (cb_item != NULL && cb_item->handler != cb) { + cb_item = cb_item->next; + } + if (!cb_item) { return -1; } + oc_list_remove(oc_network_interface_cb_list, cb_item); - int ret = net_context_sendto( - send_pkt, (struct sockaddr *)&peer_addr, sizeof(struct sockaddr_in6), - udp_sent, 0, UINT_TO_POINTER(net_pkt_get_len(send_pkt)), NULL); - if (ret < 0) { - OC_WRN("oc_send_buffer: cannot send data to peer (%d)", ret); - net_pkt_unref(send_pkt); - return ret; + oc_memb_free(&oc_network_interface_cb_s, cb_item); + return 0; +} + +void +handle_network_interface_event_callback(oc_interface_event_t event) +{ + if (oc_list_length(oc_network_interface_cb_list) > 0) { + oc_network_interface_cb_t *cb_item = + oc_list_head(oc_network_interface_cb_list); + while (cb_item) { + cb_item->handler(event); + cb_item = cb_item->next; + } } - return message->length; } +#endif /* OC_NETWORK_MONITOR */ -static void -free_endpoints(void) +#ifdef OC_SESSION_EVENTS +int +oc_add_session_event_callback(session_event_handler_t cb) { - oc_endpoint_t *ep = eps, *next; - while (ep != NULL) { - next = ep->next; - oc_free_endpoint(ep); - ep = next; + if (!cb) + return -1; + + oc_session_event_cb_t *cb_item = oc_memb_alloc(&oc_session_event_cb_s); + if (!cb_item) { + OC_ERR("Failed to allocate memory for session event callback"); + return -1; } + + cb_item->handler = cb; + oc_list_add(oc_session_event_cb_list, cb_item); + return 0; } -oc_endpoint_t * -oc_connectivity_get_endpoints(size_t device) +int +oc_remove_session_event_callback(session_event_handler_t cb) { - (void)device; - if (!eps) { - oc_endpoint_t *ep = oc_new_endpoint(); - if (!ep) { - return NULL; + if (!cb) + return -1; + + oc_session_event_cb_t *cb_item = oc_list_head(oc_session_event_cb_list); + while (cb_item != NULL && cb_item->handler != cb) { + cb_item = cb_item->next; + } + if (!cb_item) { + return -1; + } + oc_list_remove(oc_session_event_cb_list, cb_item); + + oc_memb_free(&oc_session_event_cb_s, cb_item); + return 0; +} + +void +handle_session_event_callback(const oc_endpoint_t *endpoint, + oc_session_state_t state) +{ + if (oc_list_length(oc_session_event_cb_list) > 0) { + oc_session_event_cb_t *cb_item = oc_list_head(oc_session_event_cb_list); + while (cb_item) { + cb_item->handler(endpoint, state); + cb_item = cb_item->next; } - eps = ep; - memset(ep, 0, sizeof(oc_endpoint_t)); - ep->flags = IPV6; - net_addr_pton(AF_INET6, CONFIG_NET_APP_MY_IPV6_ADDR, ep->addr.ipv6.address); - ep->addr.ipv6.port = ntohs(my_addr6.sin6_port); - ep->device = 0; + } +} +#endif /* OC_SESSION_EVENTS */ + +#ifdef OC_IPV4 +static int +connectivity_ipv4_init(ip_context_t *dev) +{ + struct sockaddr_in *addr; + OC_DBG("Initializing IPv4 connectivity for device %zd", dev->device); + + // Initialize IPv4 socket + memset(&dev->server4, 0, sizeof(struct sockaddr_storage)); + addr = (struct sockaddr_in *)&dev->server4; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = INADDR_ANY; + addr->sin_port = 0; + + dev->server4_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (dev->server4_sock < 0) { + OC_ERR("Failed to create IPv4 socket for device %zd: %d", dev->device, + errno); + return -1; + } + + if (bind(dev->server4_sock, (struct sockaddr *)&dev->server4, + sizeof(dev->server4)) == -1) { + OC_ERR("Failed to bind the IPv4 socket for device %zd: %d", dev->device, + errno); + return -1; + } + + socklen_t socklen = sizeof(dev->server4); + if (getsockname(dev->server4_sock, (struct sockaddr *)&dev->server4, + &socklen) == -1) { + OC_ERR("Failed to get the IPv4 socket name for device %zd: %d", dev->device, + errno); + return -1; + } + addr = (struct sockaddr_in *)&dev->server4; + dev->port4 = ntohs(addr->sin_port); + + // Initialize IPv4 multicast socket + memset(&dev->mcast4, 0, sizeof(struct sockaddr_storage)); + addr = (struct sockaddr_in *)&dev->mcast4; + addr->sin_family = AF_INET; + addr->sin_port = htons(OCF_PORT_UNSECURED); + addr->sin_addr.s_addr = INADDR_ANY; + + dev->mcast4_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (dev->mcast4_sock < 0) { + OC_ERR("Failed to create IPv4 multicast socket for device %zd: %d", + dev->device, errno); + return -1; + } + + int on = 1; + if (setsockopt(dev->mcast4_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == + -1) { + OC_ERR("Failed to set the reuseaddr option on the IPv4 multicast socket " + "for device %zd: %d", + dev->device, errno); + return -1; + } + + if (bind(dev->mcast4_sock, (struct sockaddr *)&dev->mcast4, + sizeof(dev->mcast4)) == -1) { + OC_ERR("Failed to bind the IPv4 multicast socket for device %zd: %d", + dev->device, errno); + return -1; + } + + if (configure_mcast_socket(dev->mcast4_sock, AF_INET) < 0) { + OC_ERR("Failed to configure the IPv4 multicast socket for device %zd", + dev->device); + return -1; + } + #ifdef OC_SECURITY - oc_endpoint_t *ep_sec = oc_new_endpoint(); - if (ep_sec) { - memset(ep_sec, 0, sizeof(oc_endpoint_t)); - ep_sec->flags = IPV6 | SECURED; - net_addr_pton(AF_INET6, CONFIG_NET_APP_MY_IPV6_ADDR, - ep_sec->addr.ipv6.address); - ep_sec->addr.ipv6.port = ntohs(dtls_addr6.sin6_port); - ep_sec->device = 0; - ep->next = ep_sec; - } + // Initialize secure IPv4 socket + memset(&dev->secure4, 0, sizeof(struct sockaddr_storage)); + addr = (struct sockaddr_in *)&dev->secure4; + addr->sin_family = AF_INET; + addr->sin_port = 0; + addr->sin_addr.s_addr = INADDR_ANY; + + dev->secure4_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (dev->secure4_sock < 0) { + OC_ERR("Failed to create secure IPv4 socket for device %zd: %d", + dev->device, errno); + return -1; + } + + if (bind(dev->secure4_sock, (struct sockaddr *)&dev->secure4, + sizeof(dev->secure4)) == -1) { + OC_ERR("Failed to bind the secure IPv4 socket for device %zd: %d", + dev->device, errno); + return -1; + } + + socklen = sizeof(dev->secure4); + if (getsockname(dev->secure4_sock, (struct sockaddr *)&dev->secure4, + &socklen) == -1) { + OC_ERR("Failed to get the secure IPv4 socket name for device %zd: %d", + dev->device, errno); + return -1; + } + addr = (struct sockaddr_in *)&dev->secure4; + dev->dtls4_port = ntohs(addr->sin_port); #endif /* OC_SECURITY */ + + OC_DBG("Successfully initialized IPv4 connectivity for device %zd", + dev->device); + return 0; +} +#endif + +static void +net_mgmt_event_if_handler(struct net_mgmt_event_callback *cb, + uint32_t mgmt_event, struct net_if *iface) +{ + switch (mgmt_event) { + case NET_EVENT_IF_UP: { + OC_DBG("event: NET_EVENT_IF_UP"); + process_interface_change_event(iface, NETWORK_INTERFACE_UP); + } break; + case NET_EVENT_IF_DOWN: { + OC_DBG("event: NET_EVENT_IF_DOWN"); + process_interface_change_event(iface, NETWORK_INTERFACE_DOWN); + } break; + default: + OC_WRN("unhandled event: 0x%x", mgmt_event); + break; } - return eps; } int oc_connectivity_init(size_t device) { - (void)device; - int ret; + struct sockaddr_in6 *addr; + OC_DBG("Initializing connectivity for device %zd", device); - /* Record OCF's multicast address with network interface */ - net_if_ipv6_maddr_add(net_if_get_default(), &in6addr_mcast); + ip_context_t *dev = (ip_context_t *)oc_memb_alloc(&ip_context_s); + if (!dev) { + oc_abort("Insufficient memory"); + } + oc_list_add(ip_contexts, dev); + dev->device = device; + OC_LIST_STRUCT_INIT(dev, eps); - net_ipaddr_copy(&mcast_addr6.sin6_addr, &in6addr_mcast); - mcast_addr6.sin6_family = AF_INET6; - mcast_addr6.sin6_port = htons(OCF_MCAST_PORT); + if (pthread_mutex_init(&dev->rfds_mutex, NULL) != 0) { + OC_ERR( + "Failed to initialize mutex for receive file descriptors for device %zd", + dev->device); + oc_abort("Failed to initialize mutex"); + } - /* Wildcard address set for server with randomly chosen port */ - my_addr6.sin6_family = AF_INET6; + if (zsock_socketpair(AF_UNIX, SOCK_STREAM, 0, dev->shutdown_pipe) < 0) { + OC_ERR("Failed to create the shutdown_pipe socketpair: %d", errno); + return -1; + } - /* Add unicast IPV6 address to interface so that node can communicate */ - /* Would be good to have the address auto-configured in case some router is - * distributing pre-fixes*/ - if (net_addr_pton(AF_INET6, CONFIG_NET_APP_MY_IPV6_ADDR, &in6addr_my) < 0) { - NET_ERR("Invalid IPv6 address %s", CONFIG_NET_APP_MY_IPV6_ADDR); + if (set_nonblock_socket(dev->shutdown_pipe[0]) < 0) { + OC_ERR("Could not set shutdown_pipe[0] to non-blocking mode: %d", errno); + return -1; } -#ifdef OC_DEBUG - struct net_if_addr *ifaddr = -#endif - net_if_ipv6_addr_add(net_if_get_default(), &in6addr_my, NET_ADDR_MANUAL, 0); -#ifdef OC_DEBUG - OC_DBG("=====>>>Interface unicast address added @ %p", ifaddr); -#endif + // Initialize IPv6 socket + memset(&dev->server, 0, sizeof(struct sockaddr_storage)); + addr = (struct sockaddr_in6 *)&dev->server; + addr->sin6_family = AF_INET6; + addr->sin6_port = 0; + addr->sin6_addr = in6addr_any; -#ifdef OC_SECURITY - dtls_addr6.sin6_port = htons(MY_DTLS_PORT); - dtls_addr6.sin6_family = AF_INET6; -#endif /* OC_SECURITY */ + dev->server_sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (dev->server_sock < 0) { + OC_ERR("Failed to create IPv6 socket for device %zd: %d", dev->device, + errno); + return -1; + } - ret = net_context_get(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, &udp_recv6); - if (ret < 0) { - OC_WRN("oc_connectivity_init: cannot get UDP network context for server" - "receive (%d)", - ret); - goto error; + int sockopt_on = 1; + if (setsockopt(dev->server_sock, IPPROTO_IPV6, IPV6_V6ONLY, &sockopt_on, + sizeof(sockopt_on)) == -1) { + OC_ERR("Failed to set the IPv6 only option on the IPv6 socket for device " + "%zd: %d", + dev->device, errno); + return -1; } - ret = net_context_bind(udp_recv6, (struct sockaddr *)&my_addr6, - sizeof(struct sockaddr_in6)); - if (ret < 0) { - OC_WRN("oc_connectivity_init: cannot bind UDP port %d to server's network" - "context (%d)", - ntohs(my_addr6.sin6_port), ret); - goto error; + if (bind(dev->server_sock, (struct sockaddr *)&dev->server, + sizeof(dev->server)) == -1) { + OC_ERR("Failed to bind the IPv6 socket for device %zd: %d", dev->device, + errno); + return -1; } - ret = net_context_get(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, &mcast_recv6); - if (ret < 0) { - OC_WRN("oc_connectivity_init: cannot get UDP network context for OCF" - "multicast receive (%d)", - ret); - goto error; + socklen_t socklen = sizeof(dev->server); + if (getsockname(dev->server_sock, (struct sockaddr *)&dev->server, + &socklen) == -1) { + OC_ERR("Failed to get the IPv6 socket name for device %zd: %d", dev->device, + errno); + return -1; } + addr = (struct sockaddr_in6 *)&dev->server; + dev->port = ntohs(addr->sin6_port); - ret = net_context_bind(mcast_recv6, (struct sockaddr *)&mcast_addr6, - sizeof(struct sockaddr_in6)); - if (ret < 0) { - OC_WRN("oc_connectivity_init: cannot bind OCF multicast network context" - "(%d)", - ret); - goto error; + // Initialize IPv6 multicast socket + memset(&dev->mcast, 0, sizeof(struct sockaddr_storage)); + addr = (struct sockaddr_in6 *)&dev->mcast; + addr->sin6_family = AF_INET6; + addr->sin6_port = htons(OCF_PORT_UNSECURED); + addr->sin6_addr = in6addr_any; + + dev->mcast_sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (dev->server_sock < 0) { + OC_ERR("Failed to create IPv6 multicast socket for device %zd: %d", + dev->device, errno); + return -1; + } + + if (setsockopt(dev->mcast_sock, SOL_SOCKET, SO_REUSEADDR, &sockopt_on, + sizeof(sockopt_on)) == -1) { + OC_ERR("Failed to set the reuseaddr option on the IPv6 multicast socket " + "for device %zd: %d", + dev->device, errno); + return -1; + } + + if (bind(dev->mcast_sock, (struct sockaddr *)&dev->mcast, + sizeof(dev->mcast)) == -1) { + OC_ERR("Failed to bind the IPv6 multicast socket for device %zd: %d", + dev->device, errno); + return -1; + } + + if (configure_mcast_socket(dev->mcast_sock, AF_INET6) < 0) { + OC_ERR("Failed to configure the IPv6 multicast socket for device %zd", + dev->device); + return -1; } #ifdef OC_SECURITY - ret = net_context_get(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, &dtls_recv6); - if (ret < 0) { - OC_WRN("oc_connectivity_init: cannot get DTLS network context" - "(%d)", - ret); - goto error; + // Initialize secure IPv6 socket + memset(&dev->secure, 0, sizeof(struct sockaddr_storage)); + addr = (struct sockaddr_in6 *)&dev->secure; + addr->sin6_family = AF_INET6; + addr->sin6_port = 0; + addr->sin6_addr = in6addr_any; + + dev->secure_sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (dev->secure_sock < 0) { + OC_ERR("Failed to create secure IPv6 socket for device %zd: %d", + dev->device, errno); + return -1; } - ret = net_context_bind(dtls_recv6, (struct sockaddr *)&dtls_addr6, - sizeof(struct sockaddr_in6)); - if (ret < 0) { - OC_WRN("oc_connectivity_init: cannot bind DTLS network context" - "(%d)", - ret); - goto error; + if (setsockopt(dev->secure_sock, IPPROTO_IPV6, IPV6_V6ONLY, &sockopt_on, + sizeof(sockopt_on)) == -1) { + OC_ERR("Failed to set the IPv6 only option on the secure IPv6 socket for " + "device %zd: %d", + dev->device, errno); + return -1; } -#endif /* OC_SECURITY */ - ret = net_context_recv(mcast_recv6, oc_network_receive, 0, NULL); - if (ret < 0) { - OC_WRN("oc_connectivity_init: net_context_recv error from multicast socket:" - "(%d)", - ret); - goto error; + if (bind(dev->secure_sock, (struct sockaddr *)&dev->secure, + sizeof(dev->secure)) == -1) { + OC_ERR("Failed to bind the secure IPv6 socket for device %zd: %d", + dev->device, errno); + return -1; } - ret = net_context_recv(udp_recv6, oc_network_receive, 0, NULL); - if (ret < 0) { - OC_WRN("oc_connectivity_init: net_context_recv error from server socket:" - "(%d)", - ret); - goto error; + socklen = sizeof(dev->secure); + if (getsockname(dev->secure_sock, (struct sockaddr *)&dev->secure, + &socklen) == -1) { + OC_ERR("Failed to get the secure IPv6 socket name for device %zd: %d", + dev->device, errno); + return -1; } + addr = (struct sockaddr_in6 *)&dev->secure; + dev->dtls_port = ntohs(addr->sin6_port); +#endif /* OC_SECURITY */ + +#ifdef OC_IPV4 + if (connectivity_ipv4_init(dev) != 0) { + OC_ERR("Failed to initialize IPv4 connectivity for device %zd", + dev->device); + return -1; + } +#endif /* OC_IPV4 */ + OC_DBG("=======ip port info.========"); + OC_DBG(" ipv6 port : %u", dev->port); #ifdef OC_SECURITY - static uint16_t dtls_port = MY_DTLS_PORT; - ret = net_context_recv(dtls_recv6, oc_network_receive, 0, &dtls_port); - if (ret < 0) { - OC_WRN("oc_connectivity_init: net_context_recv error from DTLS socket:" - "(%d)", - ret); - goto error; + OC_DBG(" ipv6 secure : %u", dev->dtls_port); +#endif +#ifdef OC_IPV4 + OC_DBG(" ipv4 port : %u", dev->port4); +#ifdef OC_SECURITY + OC_DBG(" ipv4 secure : %u", dev->dtls4_port); +#endif +#endif + +#ifdef OC_TCP + if (oc_tcp_connectivity_init(dev) != 0) { + OC_ERR("Could not initialize TCP adapter"); } -#endif /* OC_SECURITY */ +#endif /* OC_TCP */ - OC_DBG("oc_connectivity_init: successfully initialized connectivity"); - return 0; + /* Register network management event callbacks to listen for network interface + * changes. + */ + if (!ifchange_initialized) { + net_mgmt_init_event_callback(&net_mgmt_event_if_cb, + net_mgmt_event_if_handler, + NET_EVENT_IF_UP | NET_EVENT_IF_DOWN); + net_mgmt_add_event_callback(&net_mgmt_event_if_cb); +#ifdef OC_NETWORK_MONITOR + if (!check_new_ip_interfaces()) { + OC_ERR("Check for new IP interfaces failed"); + return -1; + } +#endif /* OC_NETWORK_MONITOR */ + ifchange_initialized = true; + } -error: - OC_ERR("oc_connectivity_init: failed to initialize connectivity"); - return -1; + int ret = pthread_attr_init(&oc_network_thread_attr); + if (ret != 0) { + OC_ERR("Failed to initialize network thread for device %zd: " + "pthread_attr_init() failed: %d", + dev->device, errno); + return -1; + } + ret = pthread_attr_setstack(&oc_network_thread_attr, &oc_network_thread_stack, + NETWORK_THREAD_STACKSIZE); + if (ret != 0) { + OC_ERR("Failed to initialize network thread for device %zd: " + "pthread_attr_setstack() failed: %d", + dev->device, errno); + return -1; + } + ret = pthread_create(&dev->event_thread, &oc_network_thread_attr, + &network_event_thread, dev); + if (ret != 0) { + OC_ERR("Failed to initialize network thread for device %zd: " + "pthread_create() failed: %d", + dev->device, errno); + return -1; + } + + OC_DBG("Successfully initialized connectivity for device %zd", device); + + return 0; } void oc_connectivity_shutdown(size_t device) { - (void)device; + ip_context_t *dev = get_ip_context_for_device(device); + OC_ATOMIC_STORE8(dev->terminate, 1); + if (write(dev->shutdown_pipe[1], "\n", 1) < 0) { + OC_WRN("cannot wakeup network thread"); + } + + pthread_join(dev->event_thread, NULL); + + close(dev->server_sock); + close(dev->mcast_sock); + +#ifdef OC_IPV4 + close(dev->server4_sock); + close(dev->mcast4_sock); +#endif /* OC_IPV4 */ + #ifdef OC_SECURITY - net_context_put(dtls_recv6); + close(dev->secure_sock); +#ifdef OC_IPV4 + close(dev->secure4_sock); +#endif /* OC_IPV4 */ #endif /* OC_SECURITY */ - net_context_put(udp_recv6); - net_context_put(mcast_recv6); - free_endpoints(); + +#ifdef OC_TCP + oc_tcp_connectivity_shutdown(dev); +#endif /* OC_TCP */ + + close(dev->shutdown_pipe[1]); + close(dev->shutdown_pipe[0]); + + pthread_mutex_destroy(&dev->rfds_mutex); + + free_endpoints_list(dev); + + oc_list_remove(ip_contexts, dev); + oc_memb_free(&ip_context_s, dev); } -#ifdef OC_CLIENT +#ifdef OC_TCP void -oc_send_discovery_request(oc_message_t *message) +oc_connectivity_end_session(oc_endpoint_t *endpoint) { - oc_send_buffer(message); + if (endpoint->flags & TCP) { + ip_context_t *dev = get_ip_context_for_device(endpoint->device); + if (dev) { + oc_tcp_end_session(dev, endpoint); + } + } +} +#endif /* OC_TCP */ + +#ifdef OC_DNS_LOOKUP +#ifdef OC_DNS_CACHE +typedef struct oc_dns_cache_t +{ + struct oc_dns_cache_t *next; + oc_string_t domain; + union dev_addr addr; +} oc_dns_cache_t; + +OC_MEMB(dns_s, oc_dns_cache_t, 1); +OC_LIST(dns_cache); + +static oc_dns_cache_t * +oc_dns_lookup_cache(const char *domain) +{ + if (oc_list_length(dns_cache) == 0) { + return NULL; + } + oc_dns_cache_t *c = (oc_dns_cache_t *)oc_list_head(dns_cache); + while (c) { + if (strlen(domain) == oc_string_len(c->domain) && + memcmp(domain, oc_string(c->domain), oc_string_len(c->domain)) == 0) { + return c; + } + c = c->next; + } + return NULL; +} + +static int +oc_dns_cache_domain(const char *domain, union dev_addr *addr) +{ + oc_dns_cache_t *c = (oc_dns_cache_t *)oc_memb_alloc(&dns_s); + if (c) { + oc_new_string(&c->domain, domain, strlen(domain)); + memcpy(&c->addr, addr, sizeof(union dev_addr)); + oc_list_add(dns_cache, c); + return 0; + } + return -1; +} + +void +oc_dns_clear_cache(void) +{ + oc_dns_cache_t *c = (oc_dns_cache_t *)oc_list_pop(dns_cache); + while (c) { + oc_free_string(&c->domain); + oc_memb_free(&dns_s, c); + c = (oc_dns_cache_t *)oc_list_pop(dns_cache); + } +} +#endif /* OC_DNS_CACHE */ + +int +oc_dns_lookup(const char *domain, oc_string_t *addr, enum transport_flags flags) +{ + if (!domain || !addr) { + OC_ERR("Error of input parameters"); + return -1; + } + int ret = -1; + union dev_addr a; + +#ifdef OC_DNS_CACHE + oc_dns_cache_t *c = oc_dns_lookup_cache(domain); + + if (!c) { +#endif /* OC_DNS_CACHE */ + memset(&a, 0, sizeof(union dev_addr)); + + struct addrinfo hints, *result = NULL; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = (flags & IPV6) ? AF_INET6 : AF_INET; + hints.ai_socktype = (flags & TCP) ? SOCK_STREAM : SOCK_DGRAM; + ret = getaddrinfo(domain, NULL, &hints, &result); + + if (ret == 0) { + if (flags & IPV6) { + struct sockaddr_in6 *r = (struct sockaddr_in6 *)result->ai_addr; + memcpy(a.ipv6.address, r->sin6_addr.s6_addr, + sizeof(r->sin6_addr.s6_addr)); + a.ipv6.port = ntohs(r->sin6_port); + a.ipv6.scope = r->sin6_scope_id; + } +#ifdef OC_IPV4 + else { + struct sockaddr_in *r = (struct sockaddr_in *)result->ai_addr; + memcpy(a.ipv4.address, &r->sin_addr.s_addr, sizeof(r->sin_addr.s_addr)); + a.ipv4.port = ntohs(r->sin_port); + } +#endif /* OC_IPV4 */ +#ifdef OC_DNS_CACHE + oc_dns_cache_domain(domain, &a); +#endif /* OC_DNS_CACHE */ + } + freeaddrinfo(result); +#ifdef OC_DNS_CACHE + } else { + ret = 0; + memcpy(&a, &c->addr, sizeof(union dev_addr)); + } +#endif /* OC_DNS_CACHE */ + + if (ret == 0) { + char address[INET6_ADDRSTRLEN + 2] = { 0 }; + const char *dest = NULL; + if (flags & IPV6) { + address[0] = '['; + dest = inet_ntop(AF_INET6, (void *)a.ipv6.address, address + 1, + INET6_ADDRSTRLEN); + size_t addr_len = strlen(address); + address[addr_len] = ']'; + address[addr_len + 1] = '\0'; + } +#ifdef OC_IPV4 + else { + dest = + inet_ntop(AF_INET, (void *)a.ipv4.address, address, INET_ADDRSTRLEN); + } +#endif /* OC_IPV4 */ + if (dest) { + OC_DBG("%s address is %s", domain, address); + oc_new_string(addr, address, strlen(address)); + } else { + ret = -1; + } + } + + return ret; +} +#endif /* OC_DNS_LOOKUP */ + +int +set_nonblock_socket(int sockfd) +{ + int flags = zsock_fcntl(sockfd, F_GETFL, 0); + if (flags < 0) { + return -1; + } + + return zsock_fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); +} + +void +ip_context_rfds_fd_set(ip_context_t *dev, int sockfd) +{ + pthread_mutex_lock(&dev->rfds_mutex); + FD_SET(sockfd, &dev->rfds); + pthread_mutex_unlock(&dev->rfds_mutex); +} + +void +ip_context_rfds_fd_clr(ip_context_t *dev, int sockfd) +{ + pthread_mutex_lock(&dev->rfds_mutex); + FD_CLR(sockfd, &dev->rfds); + pthread_mutex_unlock(&dev->rfds_mutex); +} + +fd_set +ip_context_rfds_fd_copy(ip_context_t *dev) +{ + fd_set setfds; + pthread_mutex_lock(&dev->rfds_mutex); + memcpy(&setfds, &dev->rfds, sizeof(dev->rfds)); + pthread_mutex_unlock(&dev->rfds_mutex); + return setfds; } -#endif /* OC_CLIENT */ diff --git a/port/zephyr/src/ipadapter.h b/port/zephyr/src/ipadapter.h new file mode 100644 index 0000000000..c037219c6d --- /dev/null +++ b/port/zephyr/src/ipadapter.h @@ -0,0 +1,23 @@ +/**************************************************************************** + * + * Copyright 2019 Jozef Kralik All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************/ +#ifndef IPADAPTER_H +#define IPADAPTER_H + +int set_nonblock_socket(int sockfd); + +#endif /* IPADAPTER_H */ \ No newline at end of file diff --git a/port/zephyr/src/ipcontext.h b/port/zephyr/src/ipcontext.h new file mode 100644 index 0000000000..ba6c378386 --- /dev/null +++ b/port/zephyr/src/ipcontext.h @@ -0,0 +1,139 @@ +/**************************************************************************** + * + * Copyright 2018 Samsung Electronics All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#ifndef IPCONTEXT_H +#define IPCONTEXT_H + +#include "oc_endpoint.h" +#include "util/oc_atomic.h" +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + ADAPTER_STATUS_NONE = 0, /* Nothing happens */ + ADAPTER_STATUS_ACCEPT, /* Receiving no meaningful data */ + ADAPTER_STATUS_RECEIVE, /* Receiving meaningful data */ + ADAPTER_STATUS_ERROR /* Error */ +} adapter_receive_state_t; + +#ifdef OC_TCP +typedef struct tcp_context_t +{ + struct sockaddr_storage server; + int server_sock; + uint16_t port; +#ifdef OC_SECURITY + struct sockaddr_storage secure; + int secure_sock; + uint16_t tls_port; +#endif /* OC_SECURITY */ +#ifdef OC_IPV4 + struct sockaddr_storage server4; + int server4_sock; + uint16_t port4; +#ifdef OC_SECURITY + struct sockaddr_storage secure4; + int secure4_sock; + uint16_t tls4_port; +#endif /* OC_SECURITY */ +#endif /* OC_IPV4 */ + int connect_pipe[2]; +} tcp_context_t; +#endif + +typedef enum { + IP_CONTEXT_FLAG_REFRESH_ENDPOINT_LIST = + 1 << 0, ///< used to signal that endpoint list needs to be refreshed +} ip_context_flags_t; + +typedef struct ip_context_t +{ + struct ip_context_t *next; + OC_LIST_STRUCT(eps); /// < not thread-safe, must be used only from main thread + struct sockaddr_storage mcast; + struct sockaddr_storage server; + int mcast_sock; + int server_sock; + uint16_t port; + uint16_t padding; +#ifdef OC_SECURITY + struct sockaddr_storage secure; + int secure_sock; + uint16_t dtls_port; +#endif /* OC_SECURITY */ +#ifdef OC_IPV4 + struct sockaddr_storage mcast4; + struct sockaddr_storage server4; + int mcast4_sock; + int server4_sock; + uint16_t port4; +#ifdef OC_SECURITY + struct sockaddr_storage secure4; + int secure4_sock; + uint16_t dtls4_port; +#endif /* OC_SECURITY */ +#endif /* OC_IPV4 */ +#ifdef OC_TCP + tcp_context_t tcp; +#endif + pthread_t event_thread; + OC_ATOMIC_INT8_T terminate; + size_t device; + pthread_mutex_t rfds_mutex; + fd_set rfds; + int shutdown_pipe[2]; + OC_ATOMIC_INT8_T flags; +} ip_context_t; + +/** + * Set a given file descriptor to a set (dev->rfds) under the mutex(rfds_mutex). + * + * @param[in] dev the device network context. + * @param[in] sockfd the file descriptor. + */ +void ip_context_rfds_fd_set(ip_context_t *dev, int sockfd); + +/** + * Remove a given file descriptor from a set (dev->rfds) under the + * mutex(rfds_mutex). + * + * @param[in] dev the device network context. + * @param[in] sockfd the file descriptor. + */ +void ip_context_rfds_fd_clr(ip_context_t *dev, int sockfd); + +/** + * Make a copy of file descriptor set (dev->rfds) under the mutex(rfds_mutex). + * + * @param[in] dev the device network context. + * + * @return a copy of file descriptor set. + */ +fd_set ip_context_rfds_fd_copy(ip_context_t *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* IPCONTEXT_H */ diff --git a/port/zephyr/src/oc_config.h b/port/zephyr/src/oc_config.h index 1da0a48205..5b8bf1d365 100644 --- a/port/zephyr/src/oc_config.h +++ b/port/zephyr/src/oc_config.h @@ -19,23 +19,63 @@ /* Time resolution */ #include + +#ifdef __cplusplus +extern "C" { +#endif + typedef uint64_t oc_clock_time_t; #include -#define OC_CLOCK_CONF_TICKS_PER_SECOND (1000) +#define OC_CLOCK_CONF_TICKS_PER_SECOND (CONFIG_SYS_CLOCK_TICKS_PER_SEC) + +/* jitter added to response to some multicast requests */ +#define OC_MULTICAST_RESPONSE_JITTER_MS (2000) + +/* Max inactivity timeout before tearing down DTLS connection */ +#define OC_DTLS_INACTIVITY_TIMEOUT (600) + +/* Add support for passing network up/down events to the app */ +#define OC_NETWORK_MONITOR +/* Add support for passing TCP/TLS/DTLS session connection events to the app */ +#define OC_SESSION_EVENTS +/* Add request history for deduplicate UDP/DTLS messages */ +#define OC_REQUEST_HISTORY + +/* Add batch interface support to /oic/res */ +#define OC_RES_BATCH_SUPPORT + +/* Add support observable for oic/res */ +//#define OC_DISCOVERY_RESOURCE_OBSERVABLE -#define OC_BYTES_POOL_SIZE (1024) -#define OC_INTS_POOL_SIZE (16) -#define OC_DOUBLES_POOL_SIZE (16) +#ifdef OC_DYNAMIC_ALLOCATION +#define OC_COLLECTIONS +#define OC_BLOCK_WISE + +/* Enable reallocation during encoding the representation to cbor or run "make" + * with REP_ENCODING_REALLOC=1 */ +//#define OC_REP_ENCODING_REALLOC + +#else /* OC_DYNAMIC_ALLOCATION */ +/* List of constraints below for a build that does not employ dynamic + memory allocation +*/ +/* Memory pool sizes */ +#define OC_BYTES_POOL_SIZE (1800) +#define OC_INTS_POOL_SIZE (100) +#define OC_DOUBLES_POOL_SIZE (4) /* Server-side parameters */ /* Maximum number of server resources */ -#define OC_MAX_APP_RESOURCES (1) +#define OC_MAX_APP_RESOURCES (4) + +#define OC_MAX_NUM_COLLECTIONS (1) /* Common parameters */ -//#define OC_BLOCK_WISE_SET_MTU (80) +/* Prescriptive lower layers MTU size, enable block-wise transfers */ +#define OC_BLOCK_WISE_SET_MTU (700) -/* Maximum size of request/response PDUs */ -#define OC_MAX_APP_DATA_SIZE (600) +/* Maximum size of request/response payloads */ +#define OC_MAX_APP_DATA_SIZE (2048) /* Maximum number of concurrent requests */ #define OC_MAX_NUM_CONCURRENT_REQUESTS (2) @@ -46,26 +86,28 @@ typedef uint64_t oc_clock_time_t; /* Number of devices on the OCF platform */ #define OC_MAX_NUM_DEVICES (1) +/* Maximum number of endpoints */ #define OC_MAX_NUM_ENDPOINTS (4) -/* Maximum size of uri for a collection resource */ -//#define OC_MAX_COLLECTIONS_INSTANCE_URI_SIZE (64) - /* Security layer */ /* Maximum number of authorized clients */ -#define OC_MAX_NUM_SUBJECTS (1) +#define OC_MAX_NUM_SUBJECTS (2) -/* Maximum number of concurrent DTLS sessions */ -#define OC_MAX_DTLS_PEERS (1) +/* Maximum number of concurrent (D)TLS sessions */ +#define OC_MAX_TLS_PEERS (1) -/* Max inactivity timeout before tearing down DTLS connection */ -#define OC_DTLS_INACTIVITY_TIMEOUT (10) +/* Maximum number of interfaces for IP adapter */ +#define OC_MAX_IP_INTERFACES (2) -/* Add request history for deduplicate UDP/DTLS messages */ -#define OC_REQUEST_HISTORY +/* Maximum number of callbacks for Network interface event monitoring */ +#define OC_MAX_NETWORK_INTERFACE_CBS (2) -/* Add support observable for oic/res */ -//#define OC_DISCOVERY_RESOURCE_OBSERVABLE +/* Maximum number of callbacks for connection of session */ +#define OC_MAX_SESSION_EVENT_CBS (2) + +#define OC_MAX_DOXM_OWNED_CBS (2) + +#endif /* !OC_DYNAMIC_ALLOCATION */ /* library features that require persistent storage */ #ifdef OC_SECURITY @@ -81,4 +123,8 @@ typedef uint64_t oc_clock_time_t; #define OC_STORAGE #endif +#ifdef __cplusplus +} +#endif + #endif /* OC_CONFIG_H */ diff --git a/port/zephyr/src/storage.c b/port/zephyr/src/storage_flash.c similarity index 100% rename from port/zephyr/src/storage.c rename to port/zephyr/src/storage_flash.c diff --git a/port/zephyr/src/storage_fs.c b/port/zephyr/src/storage_fs.c new file mode 100644 index 0000000000..4cb110aece --- /dev/null +++ b/port/zephyr/src/storage_fs.c @@ -0,0 +1,115 @@ +/* +// Copyright (c) 2022 Kistler Instruments AG, Winterthur, Switzerland +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include "port/oc_storage.h" +#include + +#ifdef OC_STORAGE +#include +LOG_MODULE_REGISTER(oc_storage, LOG_LEVEL_DBG); +#include +#include +#include +#include + +#define STORE_PATH_SIZE 64 + +static char store_path[STORE_PATH_SIZE]; +static int store_path_len; +static bool path_set = false; + +int +oc_storage_config(const char *store) +{ + LOG_DBG("oc_storage_config: %s", store); + store_path_len = strlen(store); + if (store_path_len >= STORE_PATH_SIZE) + return -ENOENT; + + memcpy(store_path, store, store_path_len); + store_path[store_path_len] = '\0'; + path_set = true; + return 0; +} + +long +oc_storage_read(const char *store, uint8_t *buf, size_t size) +{ + LOG_DBG("oc_storage_read: %s", store); + size_t store_len = strlen(store); + if (!path_set || (1 + store_len + store_path_len >= STORE_PATH_SIZE)) { + return -ENOENT; + } + + store_path[store_path_len] = '/'; + strncpy(store_path + store_path_len + 1, store, store_len); + store_path[1 + store_path_len + store_len] = '\0'; + + struct fs_file_t file; + fs_file_t_init(&file); + + int rc = fs_open(&file, store_path, FS_O_READ); + if (rc < 0) { + LOG_ERR("oc_storage_read: Cannot open %s: %d", store_path, rc); + return -EINVAL; + } + rc = fs_read(&file, buf, size); + if (rc < 0) { + LOG_ERR("oc_storage_read: Cannot read %s: %d", store_path, rc); + fs_close(&file); + return -EINVAL; + } + rc = fs_close(&file); + if (rc < 0) { + LOG_ERR("oc_storage_read: Cannot close %s: %d", store_path, rc); + } + return size; +} + +long +oc_storage_write(const char *store, uint8_t *buf, size_t size) +{ + LOG_DBG("oc_storage_read: %s", store); + size_t store_len = strlen(store); + if (!path_set || (1 + store_len + store_path_len >= STORE_PATH_SIZE)) { + return -ENOENT; + } + + store_path[store_path_len] = '/'; + strncpy(store_path + store_path_len + 1, store, store_len); + store_path[1 + store_path_len + store_len] = '\0'; + + struct fs_file_t file; + fs_file_t_init(&file); + + int rc = fs_open(&file, store_path, FS_O_CREATE | FS_O_RDWR); + if (rc < 0) { + LOG_ERR("oc_storage_read: Cannot open %s: %d", store_path, rc); + return -EINVAL; + } + rc = fs_write(&file, buf, size); + if (rc < 0) { + LOG_ERR("oc_storage_read: Cannot write %s: %d", store_path, rc); + fs_close(&file); + return -EINVAL; + } + rc = fs_close(&file); + if (rc < 0) { + LOG_ERR("oc_storage_read: Cannot close %s: %d", store_path, rc); + } + return size; +} +#endif /* OC_STORAGE */ diff --git a/security/oc_obt.c b/security/oc_obt.c index 37ed218406..1b69627902 100644 --- a/security/oc_obt.c +++ b/security/oc_obt.c @@ -3523,11 +3523,15 @@ oc_obt_general_post(oc_uuid_t *uuid, char *query, const char *url, strlen(payload_properties[i])); oc_rep_encode_int(&root_map, payload_int); } else if (strstr(payload_types[i], "float") != NULL) { +#ifndef CBOR_NO_FLOATING_POINT double payload_double = strtod(payload_values[i], NULL); oc_rep_encode_text_string(&root_map, payload_properties[i], strlen(payload_properties[i])); oc_rep_encode_double(&root_map, payload_double); +#else + PRINT("Device does not support float data type.\n"); +#endif } else if (strstr(payload_types[i], "str") != NULL) { oc_rep_encode_text_string(&root_map, payload_properties[i], strlen(payload_properties[i])); diff --git a/util/oc_atomic.h b/util/oc_atomic.h index 0846d4361d..cf9e84381e 100644 --- a/util/oc_atomic.h +++ b/util/oc_atomic.h @@ -116,6 +116,34 @@ extern "C" { #endif // defined(_WIN32) || defined(_WIN64) +#if defined(__ZEPHYR__) + +#include + +#define OC_ATOMIC +#define OC_ATOMIC_INT8_T atomic_t +#define OC_ATOMIC_UINT8_T atomic_t +#define OC_ATOMIC_INT32_T atomic_t +#define OC_ATOMIC_UINT32_T atomic_t + +#define OC_ATOMIC_LOAD32(x) atomic_get(&(x)) +#define OC_ATOMIC_STORE32(x, val) atomic_set(&(x), (val)) +#define OC_ATOMIC_INCREMENT32(x) atomic_inc(&(x)) +#define OC_ATOMIC_DECREMENT32(x) atomic_dec(&(x)) + +#define OC_ATOMIC_COMPARE_AND_SWAP32(x, expected, desired, result) \ + do { \ + (result) = atomic_cas(&(x), expected, desired); \ + } while (0) + +// aliases for compatibility +#define OC_ATOMIC_LOAD8(x) OC_ATOMIC_LOAD32(x) +#define OC_ATOMIC_STORE8(x, val) OC_ATOMIC_STORE32(x, val) +#define OC_ATOMIC_COMPARE_AND_SWAP8(x, expected, desired, result) \ + OC_ATOMIC_COMPARE_AND_SWAP32(x, expected, desired, result) + +#endif // defined(__ZEPHYR__) + // fallback to volatile on platforms without atomic support #ifndef OC_ATOMIC