From 6964e60904cbbd575afc56ab595b755f50eff033 Mon Sep 17 00:00:00 2001 From: Damian Krolik Date: Mon, 15 Jul 2024 11:06:35 +0200 Subject: [PATCH] net: openthrad: rpc: SRP client serialization Add serialization of the most used SRP client APIs. Add unit tests. Signed-off-by: Damian Krolik --- .../net/openthread/rpc/client/CMakeLists.txt | 1 + .../openthread/rpc/client/ot_rpc_srp_client.c | 408 ++++++++++++++++++ subsys/net/openthread/rpc/common/ot_rpc_ids.h | 15 + .../rpc/client/src/srp_client_suite.c | 390 +++++++++++++++++ .../net/openthread/rpc/common/test_rpc_env.h | 3 + 5 files changed, 817 insertions(+) create mode 100644 subsys/net/openthread/rpc/client/ot_rpc_srp_client.c create mode 100644 tests/subsys/net/openthread/rpc/client/src/srp_client_suite.c diff --git a/subsys/net/openthread/rpc/client/CMakeLists.txt b/subsys/net/openthread/rpc/client/CMakeLists.txt index 6d2e3a73a0ce..159ad4553fb0 100644 --- a/subsys/net/openthread/rpc/client/CMakeLists.txt +++ b/subsys/net/openthread/rpc/client/CMakeLists.txt @@ -17,6 +17,7 @@ zephyr_library_sources( ot_rpc_link.c ot_rpc_message.c ot_rpc_netdata.c + ot_rpc_srp_client.c ot_rpc_thread.c ot_rpc_udp.c ) diff --git a/subsys/net/openthread/rpc/client/ot_rpc_srp_client.c b/subsys/net/openthread/rpc/client/ot_rpc_srp_client.c new file mode 100644 index 000000000000..af813c6d3f62 --- /dev/null +++ b/subsys/net/openthread/rpc/client/ot_rpc_srp_client.c @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include + +#include + +#include + +/* + * Store the host info and registered services locally for two reasons: + * - to reduce the amount of data sent in the remote SRP client callback invocation + * by skipping the fields that are set by the client, anyway. This avoids sending + * the same data back and forth. + * - to validate the service pointers received in the SRP client callback. + */ +static otSrpClientHostInfo host_info; +static otSrpClientService *services; +static otSrpClientAutoStartCallback auto_start_cb; +static void *auto_start_ctx; +static otSrpClientCallback client_cb; +static void *client_ctx; + +static void clear_host(void) +{ + memset(&host_info, 0, sizeof(host_info)); + host_info.mState = OT_SRP_CLIENT_ITEM_STATE_REMOVED; +} + +/* Find the location of 'next' pointer that points to 'searched'. */ +otSrpClientService **find_service_prev_next(otSrpClientService *searched) +{ + otSrpClientService **prev_next = &services; + + for (otSrpClientService *s = services; s != NULL; prev_next = &s->mNext, s = s->mNext) { + if (s == searched) { + return prev_next; + } + } + + return NULL; +} + +static void calc_subtypes_space(const char *const *subtypes, size_t *out_count, + size_t *out_cbor_size) +{ + size_t count = 0; + size_t cbor_size = 0; + + for (; *subtypes; ++subtypes) { + ++count; + cbor_size += 2 + strlen(*subtypes); + } + + *out_count = count; + *out_cbor_size = cbor_size; +} + +static void calc_txt_space(const otDnsTxtEntry *txt, uint8_t num, size_t *out_cbor_size) +{ + size_t cbor_size = 0; + + for (; num; ++txt, --num) { + cbor_size += 2 + strlen(txt->mKey); + cbor_size += 2 + txt->mValueLength; + } + + *out_cbor_size = cbor_size; +} + +otError otSrpClientAddService(otInstance *aInstance, otSrpClientService *aService) +{ + struct nrf_rpc_cbor_ctx ctx; + size_t num_subtypes; + size_t subtypes_size; + size_t txt_size; + size_t cbor_buffer_size; + otError error; + + ARG_UNUSED(aInstance); + calc_subtypes_space(aService->mSubTypeLabels, &num_subtypes, &subtypes_size); + calc_txt_space(aService->mTxtEntries, aService->mNumTxtEntries, &txt_size); + + cbor_buffer_size = 1 + sizeof(uintptr_t); /* Service pointer */ + cbor_buffer_size += 2 + strlen(aService->mName); + cbor_buffer_size += 2 + strlen(aService->mInstanceName); + cbor_buffer_size += 1 + subtypes_size; /* Array of service subtypes */ + cbor_buffer_size += 1 + txt_size; /* Map of TXT entries */ + cbor_buffer_size += 1 + sizeof(aService->mPort); + cbor_buffer_size += 1 + sizeof(aService->mPriority); + cbor_buffer_size += 1 + sizeof(aService->mWeight); + cbor_buffer_size += 1 + sizeof(aService->mLease); + cbor_buffer_size += 1 + sizeof(aService->mKeyLease); + + NRF_RPC_CBOR_ALLOC(&ot_group, ctx, cbor_buffer_size); + nrf_rpc_encode_uint(&ctx, (uintptr_t)aService); + nrf_rpc_encode_str(&ctx, aService->mName, -1); + nrf_rpc_encode_str(&ctx, aService->mInstanceName, -1); + zcbor_list_start_encode(ctx.zs, num_subtypes); + + for (const char *const *subtype = aService->mSubTypeLabels; *subtype != NULL; ++subtype) { + nrf_rpc_encode_str(&ctx, *subtype, -1); + } + + zcbor_list_end_encode(ctx.zs, num_subtypes); + zcbor_map_start_encode(ctx.zs, aService->mNumTxtEntries); + + for (size_t i = 0; i < aService->mNumTxtEntries; ++i) { + nrf_rpc_encode_str(&ctx, aService->mTxtEntries[i].mKey, -1); + nrf_rpc_encode_buffer(&ctx, aService->mTxtEntries[i].mValue, + aService->mTxtEntries[i].mValueLength); + } + + zcbor_map_end_encode(ctx.zs, aService->mNumTxtEntries); + nrf_rpc_encode_uint(&ctx, aService->mPort); + nrf_rpc_encode_uint(&ctx, aService->mPriority); + nrf_rpc_encode_uint(&ctx, aService->mWeight); + nrf_rpc_encode_uint(&ctx, aService->mLease); + nrf_rpc_encode_uint(&ctx, aService->mKeyLease); + + nrf_rpc_cbor_cmd_no_err(&ot_group, OT_RPC_CMD_SRP_CLIENT_ADD_SERVICE, &ctx, + ot_rpc_decode_error, &error); + + if (error == OT_ERROR_NONE) { + /* Add the service to the local list */ + aService->mNext = services; + services = aService; + } + + return error; +} + +void otSrpClientClearHostAndServices(otInstance *aInstance) +{ + struct nrf_rpc_cbor_ctx ctx; + + ARG_UNUSED(aInstance); + NRF_RPC_CBOR_ALLOC(&ot_group, ctx, 0); + + nrf_rpc_cbor_cmd_no_err(&ot_group, OT_RPC_CMD_SRP_CLIENT_CLEAR_HOST_AND_SERVICES, &ctx, + nrf_rpc_rsp_decode_void, NULL); + + /* Clear the local data */ + clear_host(); + services = NULL; +} + +otError otSrpClientClearService(otInstance *aInstance, otSrpClientService *aService) +{ + struct nrf_rpc_cbor_ctx ctx; + otError error; + otSrpClientService **service_prev_next = find_service_prev_next(aService); + + if (!service_prev_next) { + /* The service has never been registered */ + return OT_ERROR_NOT_FOUND; + } + + ARG_UNUSED(aInstance); + NRF_RPC_CBOR_ALLOC(&ot_group, ctx, 1 + sizeof(uintptr_t)); + nrf_rpc_encode_uint(&ctx, (uintptr_t)aService); + + nrf_rpc_cbor_cmd_no_err(&ot_group, OT_RPC_CMD_SRP_CLIENT_CLEAR_SERVICE, &ctx, + ot_rpc_decode_error, &error); + + if (error == OT_ERROR_NONE) { + /* Remove the service from the local list */ + *service_prev_next = aService->mNext; + aService->mNext = NULL; + } + + return error; +} + +void otSrpClientDisableAutoStartMode(otInstance *aInstance) +{ + struct nrf_rpc_cbor_ctx ctx; + + ARG_UNUSED(aInstance); + NRF_RPC_CBOR_ALLOC(&ot_group, ctx, 0); + + nrf_rpc_cbor_cmd_no_err(&ot_group, OT_RPC_CMD_SRP_CLIENT_DISABLE_AUTO_START_MODE, &ctx, + nrf_rpc_rsp_decode_void, NULL); +} + +otError otSrpClientEnableAutoHostAddress(otInstance *aInstance) +{ + struct nrf_rpc_cbor_ctx ctx; + otError error; + + ARG_UNUSED(aInstance); + NRF_RPC_CBOR_ALLOC(&ot_group, ctx, 0); + + nrf_rpc_cbor_cmd_no_err(&ot_group, OT_RPC_CMD_SRP_CLIENT_ENABLE_AUTO_HOST_ADDR, &ctx, + ot_rpc_decode_error, &error); + + if (error == OT_ERROR_NONE) { + host_info.mAutoAddress = true; + } + + return error; +} + +void otSrpClientEnableAutoStartMode(otInstance *aInstance, otSrpClientAutoStartCallback aCallback, + void *aContext) +{ + struct nrf_rpc_cbor_ctx ctx; + + ARG_UNUSED(aInstance); + NRF_RPC_CBOR_ALLOC(&ot_group, ctx, 1); + nrf_rpc_encode_bool(&ctx, aCallback != NULL); + + nrf_rpc_cbor_cmd_no_err(&ot_group, OT_RPC_CMD_SRP_CLIENT_ENABLE_AUTO_START_MODE, &ctx, + nrf_rpc_rsp_decode_void, NULL); + + auto_start_cb = aCallback; + auto_start_ctx = aContext; +} + +otError otSrpClientRemoveHostAndServices(otInstance *aInstance, bool aRemoveKeyLease, + bool aSendUnregToServer) +{ + struct nrf_rpc_cbor_ctx ctx; + otError error; + + ARG_UNUSED(aInstance); + NRF_RPC_CBOR_ALLOC(&ot_group, ctx, 2); + nrf_rpc_encode_bool(&ctx, aRemoveKeyLease); + nrf_rpc_encode_bool(&ctx, aSendUnregToServer); + + nrf_rpc_cbor_cmd_no_err(&ot_group, OT_RPC_CMD_SRP_CLIENT_REMOVE_HOST_AND_SERVICES, &ctx, + ot_rpc_decode_error, &error); + + return error; +} + +otError otSrpClientRemoveService(otInstance *aInstance, otSrpClientService *aService) +{ + struct nrf_rpc_cbor_ctx ctx; + otError error; + + ARG_UNUSED(aInstance); + NRF_RPC_CBOR_ALLOC(&ot_group, ctx, 1 + sizeof(uintptr_t)); + nrf_rpc_encode_uint(&ctx, (uintptr_t)aService); + + nrf_rpc_cbor_cmd_no_err(&ot_group, OT_RPC_CMD_SRP_CLIENT_REMOVE_SERVICE, &ctx, + ot_rpc_decode_error, &error); + + return error; +} + +void otSrpClientSetCallback(otInstance *aInstance, otSrpClientCallback aCallback, void *aContext) +{ + struct nrf_rpc_cbor_ctx ctx; + + ARG_UNUSED(aInstance); + NRF_RPC_CBOR_ALLOC(&ot_group, ctx, 1); + nrf_rpc_encode_bool(&ctx, aCallback != NULL); + + nrf_rpc_cbor_cmd_no_err(&ot_group, OT_RPC_CMD_SRP_CLIENT_SET_CALLBACK, &ctx, + nrf_rpc_rsp_decode_void, NULL); + + client_cb = aCallback; + client_ctx = aContext; +} + +otError otSrpClientSetHostName(otInstance *aInstance, const char *aName) +{ + struct nrf_rpc_cbor_ctx ctx; + otError error; + + if (aName == NULL) { + return OT_ERROR_INVALID_ARGS; + } + + ARG_UNUSED(aInstance); + NRF_RPC_CBOR_ALLOC(&ot_group, ctx, 2 + strlen(aName)); + nrf_rpc_encode_str(&ctx, aName, -1); + + nrf_rpc_cbor_cmd_no_err(&ot_group, OT_RPC_CMD_SRP_CLIENT_SET_HOSTNAME, &ctx, + ot_rpc_decode_error, &error); + + if (error == OT_ERROR_NONE) { + host_info.mName = aName; + } + + return error; +} + +void otSrpClientSetKeyLeaseInterval(otInstance *aInstance, uint32_t aInterval) +{ + struct nrf_rpc_cbor_ctx ctx; + + ARG_UNUSED(aInstance); + NRF_RPC_CBOR_ALLOC(&ot_group, ctx, 1 + sizeof(aInterval)); + nrf_rpc_encode_uint(&ctx, aInterval); + + nrf_rpc_cbor_cmd_no_err(&ot_group, OT_RPC_CMD_SRP_CLIENT_SET_KEY_LEASE_INTERVAL, &ctx, + nrf_rpc_rsp_decode_void, NULL); +} + +void otSrpClientSetLeaseInterval(otInstance *aInstance, uint32_t aInterval) +{ + struct nrf_rpc_cbor_ctx ctx; + + ARG_UNUSED(aInstance); + NRF_RPC_CBOR_ALLOC(&ot_group, ctx, 1 + sizeof(aInterval)); + nrf_rpc_encode_uint(&ctx, aInterval); + + nrf_rpc_cbor_cmd_no_err(&ot_group, OT_RPC_CMD_SRP_CLIENT_SET_LEASE_INTERVAL, &ctx, + nrf_rpc_rsp_decode_void, NULL); +} + +void otSrpClientSetTtl(otInstance *aInstance, uint32_t aTtl) +{ + struct nrf_rpc_cbor_ctx ctx; + + ARG_UNUSED(aInstance); + NRF_RPC_CBOR_ALLOC(&ot_group, ctx, 1 + sizeof(aTtl)); + nrf_rpc_encode_uint(&ctx, aTtl); + + nrf_rpc_cbor_cmd_no_err(&ot_group, OT_RPC_CMD_SRP_CLIENT_SET_TTL, &ctx, + nrf_rpc_rsp_decode_void, NULL); +} + +static void ot_rpc_cmd_srp_client_cb(const struct nrf_rpc_group *group, + struct nrf_rpc_cbor_ctx *ctx, void *handler_data) +{ + otError error; + otSrpClientService *service; + otSrpClientItemState state; + otSrpClientService **service_prev_next; + otSrpClientService *removed_services = NULL; + + error = nrf_rpc_decode_uint(ctx); + host_info.mState = nrf_rpc_decode_uint(ctx); + + while (!nrf_rpc_decode_is_null(ctx)) { + service = (otSrpClientService *)nrf_rpc_decode_uint(ctx); + state = nrf_rpc_decode_uint(ctx); + + if (!nrf_rpc_decode_valid(ctx)) { + break; + } + + service_prev_next = find_service_prev_next(service); + + if (!service_prev_next) { + /* Provided service pointer unknown to the client */ + nrf_rpc_cbor_decoding_done(&ot_group, ctx); + ot_rpc_report_decoding_error(OT_RPC_CMD_SRP_CLIENT_CB); + return; + } + + service->mState = state; + + if (state == OT_SRP_CLIENT_ITEM_STATE_REMOVED) { + /* Move the service to the 'removed' list */ + *service_prev_next = service->mNext; + service->mNext = removed_services; + removed_services = service; + } + } + + if (!nrf_rpc_decoding_done_and_check(group, ctx)) { + ot_rpc_report_decoding_error(OT_RPC_CMD_SRP_CLIENT_CB); + return; + } + + if (client_cb != NULL) { + client_cb(error, &host_info, services, removed_services, client_ctx); + } + + nrf_rpc_rsp_send_void(group); +} + +NRF_RPC_CBOR_CMD_DECODER(ot_group, ot_rpc_cmd_srp_client_cb, OT_RPC_CMD_SRP_CLIENT_CB, + ot_rpc_cmd_srp_client_cb, NULL); + +static void ot_rpc_cmd_srp_client_auto_start_cb(const struct nrf_rpc_group *group, + struct nrf_rpc_cbor_ctx *ctx, void *handler_data) +{ + otSockAddr addr; + + nrf_rpc_decode_buffer(ctx, addr.mAddress.mFields.m8, OT_IP6_ADDRESS_SIZE); + addr.mPort = nrf_rpc_decode_uint(ctx); + + if (!nrf_rpc_decoding_done_and_check(group, ctx)) { + ot_rpc_report_decoding_error(OT_RPC_CMD_SRP_CLIENT_AUTO_START_CB); + return; + } + + if (auto_start_cb != NULL) { + auto_start_cb(&addr, auto_start_ctx); + } + + nrf_rpc_rsp_send_void(group); +} + +NRF_RPC_CBOR_CMD_DECODER(ot_group, ot_rpc_cmd_srp_client_auto_start_cb, + OT_RPC_CMD_SRP_CLIENT_AUTO_START_CB, ot_rpc_cmd_srp_client_auto_start_cb, + NULL); diff --git a/subsys/net/openthread/rpc/common/ot_rpc_ids.h b/subsys/net/openthread/rpc/common/ot_rpc_ids.h index b35d212ab4ff..eb689d03dec3 100644 --- a/subsys/net/openthread/rpc/common/ot_rpc_ids.h +++ b/subsys/net/openthread/rpc/common/ot_rpc_ids.h @@ -18,6 +18,8 @@ enum ot_rpc_cmd_client { OT_RPC_CMD_COAP_RESOURCE_HANDLER, OT_RPC_CMD_COAP_DEFAULT_HANDLER, OT_RPC_CMD_COAP_RESPONSE_HANDLER, + OT_RPC_CMD_SRP_CLIENT_AUTO_START_CB, + OT_RPC_CMD_SRP_CLIENT_CB, }; /** @brief Command IDs accepted by the OpenThread over RPC server. @@ -104,6 +106,19 @@ enum ot_rpc_cmd_server { OT_RPC_CMD_NETDATA_GET, OT_RPC_CMD_NETDATA_GET_NEXT_ON_MESH_PREFIX, OT_RPC_CMD_NETDATA_GET_NEXT_SERVICE, + OT_RPC_CMD_SRP_CLIENT_ADD_SERVICE, + OT_RPC_CMD_SRP_CLIENT_CLEAR_HOST_AND_SERVICES, + OT_RPC_CMD_SRP_CLIENT_CLEAR_SERVICE, + OT_RPC_CMD_SRP_CLIENT_DISABLE_AUTO_START_MODE, + OT_RPC_CMD_SRP_CLIENT_ENABLE_AUTO_HOST_ADDR, + OT_RPC_CMD_SRP_CLIENT_ENABLE_AUTO_START_MODE, + OT_RPC_CMD_SRP_CLIENT_REMOVE_HOST_AND_SERVICES, + OT_RPC_CMD_SRP_CLIENT_REMOVE_SERVICE, + OT_RPC_CMD_SRP_CLIENT_SET_CALLBACK, + OT_RPC_CMD_SRP_CLIENT_SET_HOSTNAME, + OT_RPC_CMD_SRP_CLIENT_SET_KEY_LEASE_INTERVAL, + OT_RPC_CMD_SRP_CLIENT_SET_LEASE_INTERVAL, + OT_RPC_CMD_SRP_CLIENT_SET_TTL, }; #endif /* OT_RPC_IDS_H_ */ diff --git a/tests/subsys/net/openthread/rpc/client/src/srp_client_suite.c b/tests/subsys/net/openthread/rpc/client/src/srp_client_suite.c new file mode 100644 index 000000000000..3670d0448359 --- /dev/null +++ b/tests/subsys/net/openthread/rpc/client/src/srp_client_suite.c @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* Common test data */ + +#define SERVICE_TYPE '_', 't', 'y', 'p', 'e' +#define SERVICE_INSTANCE 'i', 'n', 's', 't' +#define SERVICE_SUBTYPE1 '_', 'S', '1' +#define SERVICE_SUBTYPE2 '_', 'S', '2' +#define SERVICE_TXT1_KEY 'T', '1' +#define SERVICE_TXT1_VAL 0xff, 0xff, 0xff, 0x01 +#define SERVICE_TXT2_KEY 'T', '2' +#define SERVICE_TXT2_VAL 0xff, 0xff, 0xff, 0x02 +#define SERVICE_PRIORITY UINT16_MAX +#define SERVICE_WEIGHT (UINT16_MAX - 1) +#define SERVICE_LEASE UINT32_MAX +#define SERVICE_KEY_LEASE (UINT32_MAX - 1) + +/* + * NOTE: + * The following service encoding is correct assuming that zcbor encodes arrays and maps + * with indefinite lengths, which is true unless ZCBOR_CANONICAL Kconfig is not selected. + */ + +/* clang-format off */ +#define CBOR_SERVICE \ + /* Service type: */ \ + 0x65, SERVICE_TYPE, \ + /* Service instance: */ \ + 0x64, SERVICE_INSTANCE, \ + /* Subtypes: */ \ + 0x9f, \ + 0x63, SERVICE_SUBTYPE1, \ + 0x63, SERVICE_SUBTYPE2, \ + 0xff, \ + /* TXT: */ \ + 0xbf, \ + 0x62, SERVICE_TXT1_KEY, 0x44, SERVICE_TXT1_VAL, \ + 0x62, SERVICE_TXT2_KEY, 0x44, SERVICE_TXT2_VAL, \ + 0xff, \ + /* Other fields: */ \ + CBOR_UINT16(PORT_1), \ + CBOR_UINT16(SERVICE_PRIORITY), \ + CBOR_UINT16(SERVICE_WEIGHT), \ + CBOR_UINT32(SERVICE_LEASE), \ + CBOR_UINT32(SERVICE_KEY_LEASE) +/* clang-format on */ + +#define MAKE_CSTR(...) ((char[]){__VA_ARGS__ __VA_OPT__(,) '\0'}) +#define MAKE_BYTE_ARRAY(...) ((uint8_t[]){__VA_ARGS__}) + +/* Fake functions */ + +FAKE_VOID_FUNC(ot_srp_client_auto_start_cb, const otSockAddr *, void *); +FAKE_VOID_FUNC(ot_srp_client_cb, otError, const otSrpClientHostInfo *, const otSrpClientService *, + const otSrpClientService *, void *); + +static void nrf_rpc_err_handler(const struct nrf_rpc_err_report *report) +{ + zassert_ok(report->code); +} + +static void *tc_setup(void) +{ + mock_nrf_rpc_tr_expect_add(RPC_INIT_REQ, RPC_INIT_RSP); + zassert_ok(nrf_rpc_init(nrf_rpc_err_handler)); + mock_nrf_rpc_tr_expect_reset(); + + return NULL; +} + +static void tc_after(void *f) +{ + /* Clear all host and service data after each test case */ + mock_nrf_rpc_tr_expect_reset(); + mock_nrf_rpc_tr_expect_add(RPC_CMD(OT_RPC_CMD_SRP_CLIENT_CLEAR_HOST_AND_SERVICES), + RPC_RSP()); + otSrpClientClearHostAndServices(NULL); + mock_nrf_rpc_tr_expect_done(); +} + +/* Test serialization of otSrpClientAddService() followed by otSrpClientClearService() */ +ZTEST(ot_rpc_srp_client, test_otSrpClientAddService_otSrpClientClearService) +{ + const char * const sub_types[] = { + MAKE_CSTR(SERVICE_SUBTYPE1), + MAKE_CSTR(SERVICE_SUBTYPE2), + NULL, + }; + + const otDnsTxtEntry txt_entries[] = { + { + .mKey = MAKE_CSTR(SERVICE_TXT1_KEY), + .mValue = MAKE_BYTE_ARRAY(SERVICE_TXT1_VAL), + .mValueLength = sizeof(MAKE_BYTE_ARRAY(SERVICE_TXT1_VAL)), + }, + { + .mKey = MAKE_CSTR(SERVICE_TXT2_KEY), + .mValue = MAKE_BYTE_ARRAY(SERVICE_TXT2_VAL), + .mValueLength = sizeof(MAKE_BYTE_ARRAY(SERVICE_TXT2_VAL)), + }, + }; + + otError error; + otSrpClientService service; + + service.mName = MAKE_CSTR(SERVICE_TYPE); + service.mInstanceName = MAKE_CSTR(SERVICE_INSTANCE); + service.mSubTypeLabels = sub_types; + service.mTxtEntries = txt_entries; + service.mPort = PORT_1; + service.mPriority = SERVICE_PRIORITY; + service.mWeight = SERVICE_WEIGHT; + service.mNumTxtEntries = ARRAY_SIZE(txt_entries); + service.mLease = SERVICE_LEASE; + service.mKeyLease = SERVICE_KEY_LEASE; + + /* Test serialization of otSrpClientAddService() */ + mock_nrf_rpc_tr_expect_add(RPC_CMD(OT_RPC_CMD_SRP_CLIENT_ADD_SERVICE, + CBOR_UINT32((uintptr_t)&service), CBOR_SERVICE), + RPC_RSP(OT_ERROR_NONE)); + error = otSrpClientAddService(NULL, &service); + mock_nrf_rpc_tr_expect_done(); + + zassert_equal(error, OT_ERROR_NONE); + + /* Test serialization of otSrpClientClearService() */ + mock_nrf_rpc_tr_expect_add( + RPC_CMD(OT_RPC_CMD_SRP_CLIENT_CLEAR_SERVICE, CBOR_UINT32((uintptr_t)&service)), + RPC_RSP(OT_ERROR_NONE)); + error = otSrpClientClearService(NULL, &service); + mock_nrf_rpc_tr_expect_done(); + + zassert_equal(error, OT_ERROR_NONE); + + /* Verify that clearing the service that has not been registered results in error */ + error = otSrpClientClearService(NULL, &service); + mock_nrf_rpc_tr_expect_done(); + + zassert_equal(error, OT_ERROR_NOT_FOUND); +} + +/* Test serialization of otSrpClientAddService() and SRP client callback */ +ZTEST(ot_rpc_srp_client, test_otSrpClientAddService_callback) +{ + const char * const sub_types[] = { + MAKE_CSTR(SERVICE_SUBTYPE1), + MAKE_CSTR(SERVICE_SUBTYPE2), + NULL, + }; + + const otDnsTxtEntry txt_entries[] = { + { + .mKey = MAKE_CSTR(SERVICE_TXT1_KEY), + .mValue = MAKE_BYTE_ARRAY(SERVICE_TXT1_VAL), + .mValueLength = sizeof(MAKE_BYTE_ARRAY(SERVICE_TXT1_VAL)), + }, + { + .mKey = MAKE_CSTR(SERVICE_TXT2_KEY), + .mValue = MAKE_BYTE_ARRAY(SERVICE_TXT2_VAL), + .mValueLength = sizeof(MAKE_BYTE_ARRAY(SERVICE_TXT2_VAL)), + }, + }; + + otError error; + otSrpClientService service; + + service.mName = MAKE_CSTR(SERVICE_TYPE); + service.mInstanceName = MAKE_CSTR(SERVICE_INSTANCE); + service.mSubTypeLabels = sub_types; + service.mTxtEntries = txt_entries; + service.mPort = PORT_1; + service.mPriority = SERVICE_PRIORITY; + service.mWeight = SERVICE_WEIGHT; + service.mNumTxtEntries = ARRAY_SIZE(txt_entries); + service.mLease = SERVICE_LEASE; + service.mKeyLease = SERVICE_KEY_LEASE; + + /* Test serialization of otSrpClientAddService() */ + mock_nrf_rpc_tr_expect_add(RPC_CMD(OT_RPC_CMD_SRP_CLIENT_ADD_SERVICE, + CBOR_UINT32((uintptr_t)&service), CBOR_SERVICE), + RPC_RSP(OT_ERROR_NONE)); + error = otSrpClientAddService(NULL, &service); + mock_nrf_rpc_tr_expect_done(); + + zassert_equal(error, OT_ERROR_NONE); + + /* Test serialization of otSrpClientSetCallback() that takes non-null callback */ + mock_nrf_rpc_tr_expect_add(RPC_CMD(OT_RPC_CMD_SRP_CLIENT_SET_CALLBACK, CBOR_TRUE), + RPC_RSP()); + otSrpClientSetCallback(NULL, ot_srp_client_cb, (void *)UINT32_MAX); + mock_nrf_rpc_tr_expect_done(); + + /* Test remote call of the client callback that reports registered host & service */ + RESET_FAKE(ot_srp_client_cb); + + mock_nrf_rpc_tr_expect_add(RPC_RSP(), NO_RSP); + mock_nrf_rpc_tr_receive(RPC_CMD( + OT_RPC_CMD_SRP_CLIENT_CB, OT_ERROR_NONE, OT_SRP_CLIENT_ITEM_STATE_REGISTERED, + CBOR_UINT32((uintptr_t)&service), OT_SRP_CLIENT_ITEM_STATE_REGISTERED)); + mock_nrf_rpc_tr_expect_done(); + + zassert_equal(ot_srp_client_cb_fake.call_count, 1); + zexpect_equal(ot_srp_client_cb_fake.arg0_val, OT_ERROR_NONE); + zassert_not_null(ot_srp_client_cb_fake.arg1_val); + zexpect_equal(ot_srp_client_cb_fake.arg1_val->mState, OT_SRP_CLIENT_ITEM_STATE_REGISTERED); + zexpect_equal(ot_srp_client_cb_fake.arg2_val, &service); + zexpect_equal(ot_srp_client_cb_fake.arg2_val->mState, OT_SRP_CLIENT_ITEM_STATE_REGISTERED); + zexpect_is_null(ot_srp_client_cb_fake.arg2_val->mNext); + zexpect_is_null(ot_srp_client_cb_fake.arg3_val); + + /* Test remote call of the client callback that reports removed service */ + RESET_FAKE(ot_srp_client_cb); + + mock_nrf_rpc_tr_expect_add(RPC_RSP(), NO_RSP); + mock_nrf_rpc_tr_receive(RPC_CMD( + OT_RPC_CMD_SRP_CLIENT_CB, OT_ERROR_NONE, OT_SRP_CLIENT_ITEM_STATE_REGISTERED, + CBOR_UINT32((uintptr_t)&service), OT_SRP_CLIENT_ITEM_STATE_REMOVED)); + mock_nrf_rpc_tr_expect_done(); + + zassert_equal(ot_srp_client_cb_fake.call_count, 1); + zexpect_equal(ot_srp_client_cb_fake.arg0_val, OT_ERROR_NONE); + zassert_not_null(ot_srp_client_cb_fake.arg1_val); + zexpect_equal(ot_srp_client_cb_fake.arg1_val->mState, OT_SRP_CLIENT_ITEM_STATE_REGISTERED); + zexpect_is_null(ot_srp_client_cb_fake.arg2_val); + zexpect_equal(ot_srp_client_cb_fake.arg3_val, &service); + zexpect_equal(ot_srp_client_cb_fake.arg3_val->mState, OT_SRP_CLIENT_ITEM_STATE_REMOVED); + zexpect_is_null(ot_srp_client_cb_fake.arg3_val->mNext); + + /* Test serialization of otSrpClientSetCallback() that takes null callback */ + mock_nrf_rpc_tr_expect_add(RPC_CMD(OT_RPC_CMD_SRP_CLIENT_SET_CALLBACK, CBOR_FALSE), + RPC_RSP()); + otSrpClientSetCallback(NULL, NULL, NULL); + mock_nrf_rpc_tr_expect_done(); +} + +/* Test serialization of otSrpClientClearHostAndServices() */ +ZTEST(ot_rpc_srp_client, test_otSrpClientClearHostAndServices) +{ + mock_nrf_rpc_tr_expect_add(RPC_CMD(OT_RPC_CMD_SRP_CLIENT_CLEAR_HOST_AND_SERVICES), + RPC_RSP()); + otSrpClientClearHostAndServices(NULL); + mock_nrf_rpc_tr_expect_done(); +} + +/* Test serialization of otSrpClientDisableAutoStartMode() */ +ZTEST(ot_rpc_srp_client, test_otSrpClientDisableAutoStartMode) +{ + mock_nrf_rpc_tr_expect_add(RPC_CMD(OT_RPC_CMD_SRP_CLIENT_DISABLE_AUTO_START_MODE), + RPC_RSP()); + otSrpClientDisableAutoStartMode(NULL); + mock_nrf_rpc_tr_expect_done(); +} + +/* Test serialization of otSrpClientEnableAutoHostAddress() */ +ZTEST(ot_rpc_srp_client, test_otSrpClientEnableAutoHostAddress) +{ + otError error; + + mock_nrf_rpc_tr_expect_add(RPC_CMD(OT_RPC_CMD_SRP_CLIENT_ENABLE_AUTO_HOST_ADDR), + RPC_RSP(OT_ERROR_INVALID_STATE)); + error = otSrpClientEnableAutoHostAddress(NULL); + mock_nrf_rpc_tr_expect_done(); + + zassert_equal(error, OT_ERROR_INVALID_STATE); +} + +static void ot_srp_client_auto_start_cb_custom(const otSockAddr *addr, void *context) +{ + zassert_not_null(addr); + zexpect_mem_equal(addr->mAddress.mFields.m8, (uint8_t[]){ADDR_1}, OT_IP6_ADDRESS_SIZE); + zexpect_equal(addr->mPort, PORT_1); + zexpect_equal(context, (void *)UINT32_MAX); +} + +/* Test serialization of otSrpClientEnableAutoStartMode() */ +ZTEST(ot_rpc_srp_client, test_otSrpClientEnableAutoStartMode) +{ + /* Test serialization of otSrpClientEnableAutoStartMode() that takes non-null callback */ + mock_nrf_rpc_tr_expect_add(RPC_CMD(OT_RPC_CMD_SRP_CLIENT_ENABLE_AUTO_START_MODE, CBOR_TRUE), + RPC_RSP()); + otSrpClientEnableAutoStartMode(NULL, ot_srp_client_auto_start_cb, (void *)UINT32_MAX); + mock_nrf_rpc_tr_expect_done(); + + /* Test remote call of the auto start callback */ + RESET_FAKE(ot_srp_client_auto_start_cb); + ot_srp_client_auto_start_cb_fake.custom_fake = ot_srp_client_auto_start_cb_custom; + + mock_nrf_rpc_tr_expect_add(RPC_RSP(), NO_RSP); + mock_nrf_rpc_tr_receive( + RPC_CMD(OT_RPC_CMD_SRP_CLIENT_AUTO_START_CB, 0x50, ADDR_1, CBOR_UINT32(PORT_1))); + mock_nrf_rpc_tr_expect_done(); + + zassert_equal(ot_srp_client_auto_start_cb_fake.call_count, 1); + + /* Test serialization of otSrpClientEnableAutoStartMode() that takes null callback + */ + mock_nrf_rpc_tr_expect_add( + RPC_CMD(OT_RPC_CMD_SRP_CLIENT_ENABLE_AUTO_START_MODE, CBOR_FALSE), RPC_RSP()); + otSrpClientEnableAutoStartMode(NULL, NULL, NULL); + mock_nrf_rpc_tr_expect_done(); +} + +/* Test serialization of otSrpClientRemoveHostAndServices() */ +ZTEST(ot_rpc_srp_client, test_otSrpClientRemoveHostAndServices) +{ + otError error; + + mock_nrf_rpc_tr_expect_add( + RPC_CMD(OT_RPC_CMD_SRP_CLIENT_REMOVE_HOST_AND_SERVICES, CBOR_FALSE, CBOR_TRUE), + RPC_RSP(OT_ERROR_INVALID_STATE)); + error = otSrpClientRemoveHostAndServices(NULL, false, true); + mock_nrf_rpc_tr_expect_done(); + + zassert_equal(error, OT_ERROR_INVALID_STATE); +} + +/* Test serialization of otSrpClientRemoveService() */ +ZTEST(ot_rpc_srp_client, test_otSrpClientRemoveService) +{ + otSrpClientService *service = (otSrpClientService *)UINT32_MAX; + otError error; + + mock_nrf_rpc_tr_expect_add( + RPC_CMD(OT_RPC_CMD_SRP_CLIENT_REMOVE_SERVICE, CBOR_UINT32(UINT32_MAX)), + RPC_RSP(OT_ERROR_INVALID_STATE)); + error = otSrpClientRemoveService(NULL, service); + mock_nrf_rpc_tr_expect_done(); + + zassert_equal(error, OT_ERROR_INVALID_STATE); +} + +/* Test serialization of otSrpClientSetHostName() */ +ZTEST(ot_rpc_srp_client, test_otSrpClientSetHostName) +{ + static const char hostname[] = {DNS_NAME, '\0'}; + otError error; + + mock_nrf_rpc_tr_expect_add(RPC_CMD(OT_RPC_CMD_SRP_CLIENT_SET_HOSTNAME, CBOR_DNS_NAME), + RPC_RSP(OT_ERROR_INVALID_STATE)); + error = otSrpClientSetHostName(NULL, hostname); + mock_nrf_rpc_tr_expect_done(); + + zassert_equal(error, OT_ERROR_INVALID_STATE); +} + +/* Test serialization of otSrpClientSetKeyLeaseInterval() */ +ZTEST(ot_rpc_srp_client, test_otSrpClientSetKeyLeaseInterval) +{ + mock_nrf_rpc_tr_expect_add( + RPC_CMD(OT_RPC_CMD_SRP_CLIENT_SET_KEY_LEASE_INTERVAL, CBOR_UINT32(UINT32_MAX)), + RPC_RSP()); + otSrpClientSetKeyLeaseInterval(NULL, UINT32_MAX); + mock_nrf_rpc_tr_expect_done(); +} + +/* Test serialization of otSrpClientSetLeaseInterval() */ +ZTEST(ot_rpc_srp_client, test_otSrpClientSetLeaseInterval) +{ + mock_nrf_rpc_tr_expect_add( + RPC_CMD(OT_RPC_CMD_SRP_CLIENT_SET_LEASE_INTERVAL, CBOR_UINT32(UINT32_MAX)), + RPC_RSP()); + otSrpClientSetLeaseInterval(NULL, UINT32_MAX); + mock_nrf_rpc_tr_expect_done(); +} + +/* Test serialization of otSrpClientSetTtl() */ +ZTEST(ot_rpc_srp_client, test_otSrpClientSetTtl) +{ + mock_nrf_rpc_tr_expect_add(RPC_CMD(OT_RPC_CMD_SRP_CLIENT_SET_TTL, CBOR_UINT32(UINT32_MAX)), + RPC_RSP()); + otSrpClientSetTtl(NULL, UINT32_MAX); + mock_nrf_rpc_tr_expect_done(); +} + +ZTEST_SUITE(ot_rpc_srp_client, NULL, tc_setup, NULL, tc_after, NULL); diff --git a/tests/subsys/net/openthread/rpc/common/test_rpc_env.h b/tests/subsys/net/openthread/rpc/common/test_rpc_env.h index 8458ded422b6..d617c28ad2da 100644 --- a/tests/subsys/net/openthread/rpc/common/test_rpc_env.h +++ b/tests/subsys/net/openthread/rpc/common/test_rpc_env.h @@ -51,6 +51,8 @@ #define PORT_1 0xff01 #define PORT_2 0xff02 #define HOP_LIMIT 64 +#define DNS_NAME \ + STR_SEQUENCE(63), '.', STR_SEQUENCE(63), '.', STR_SEQUENCE(63), '.', STR_SEQUENCE(63) #define MADDR_FF02_1 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 #define EXT_ADDR 0x48, INT_SEQUENCE(OT_EXT_ADDRESS_SIZE) @@ -75,6 +77,7 @@ #define CBOR_MSG_INFO \ 0x50, ADDR_1, 0x50, ADDR_2, CBOR_UINT16(PORT_1), CBOR_UINT16(PORT_2), CBOR_UINT8(64), 3, \ CBOR_TRUE, CBOR_TRUE, CBOR_TRUE +#define CBOR_DNS_NAME 0x78, 0xff, DNS_NAME #define CBOR_ADDR1 0x50, ADDR_1 #define CBOR_SOC_ADDR CBOR_ADDR1, CBOR_UINT16(1024)