diff --git a/tests/net/lib/dns_resolve/Makefile b/tests/net/lib/dns_resolve/Makefile new file mode 100644 index 00000000000..eca21e4e4fa --- /dev/null +++ b/tests/net/lib/dns_resolve/Makefile @@ -0,0 +1,4 @@ +BOARD ?= qemu_x86 +CONF_FILE ?= prj.conf + +include $(ZEPHYR_BASE)/Makefile.test diff --git a/tests/net/lib/dns_resolve/prj.conf b/tests/net/lib/dns_resolve/prj.conf new file mode 100644 index 00000000000..515b9fac9f1 --- /dev/null +++ b/tests/net/lib/dns_resolve/prj.conf @@ -0,0 +1,31 @@ +CONFIG_NETWORKING=y + +CONFIG_RANDOM_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y + +CONFIG_NET_L2_DUMMY=y + +CONFIG_DNS_RESOLVER=y +CONFIG_DNS_RESOLVER_MAX_SERVERS=4 +CONFIG_DNS_NUM_OF_CONCUR_QUERIES=1 + +CONFIG_DNS_SERVER_IP_ADDRESSES=y +CONFIG_DNS_SERVER1="192.0.2.2" +CONFIG_DNS_SERVER2="2001:db8::2" +CONFIG_DNS_SERVER3="192.0.2.2:5353" +CONFIG_DNS_SERVER4="[2001:db8::2]:5353" + +CONFIG_NET_LOG=y +CONFIG_SYS_LOG_NET_LEVEL=4 +CONFIG_SYS_LOG_SHOW_COLOR=y +#CONFIG_NET_DEBUG_DNS_RESOLVE=y + +CONFIG_NET_IPV4=y +CONFIG_NET_IPV6=y +CONFIG_NET_IPV6_DAD=n +CONFIG_NET_IPV6_MLD=n +CONFIG_NET_IPV6_ND=n +CONFIG_NET_ARP=n + +CONFIG_PRINTK=y +CONFIG_ZTEST=y diff --git a/tests/net/lib/dns_resolve/src/Makefile b/tests/net/lib/dns_resolve/src/Makefile new file mode 100644 index 00000000000..8e1915d3033 --- /dev/null +++ b/tests/net/lib/dns_resolve/src/Makefile @@ -0,0 +1,7 @@ +ccflags-y += -I$(ZEPHYR_BASE)/subsys/net/lib/dns +ccflags-y += -I${ZEPHYR_BASE}/subsys/net/ip +ccflags-y += -I${ZEPHYR_BASE}/tests/include + +include $(ZEPHYR_BASE)/tests/Makefile.test + +obj-y = main.o diff --git a/tests/net/lib/dns_resolve/src/main.c b/tests/net/lib/dns_resolve/src/main.c new file mode 100644 index 00000000000..b441815e102 --- /dev/null +++ b/tests/net/lib/dns_resolve/src/main.c @@ -0,0 +1,656 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#define NET_LOG_ENABLED 1 +#include "net_private.h" + +#if defined(CONFIG_NET_DEBUG_DNS_RESOLVE) +#define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__) +#else +#define DBG(fmt, ...) +#endif + +#define NAME4 "4.zephyr.test" +#define NAME6 "6.zephyr.test" + +#define DNS_TIMEOUT 500 /* ms */ + +#if defined(CONFIG_NET_IPV6) +/* Interface 1 addresses */ +static struct in6_addr my_addr1 = { { { 0x20, 0x01, 0x0d, 0xb8, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; + +/* Extra address is assigned to ll_addr */ +static struct in6_addr ll_addr = { { { 0xfe, 0x80, 0x43, 0xb8, 0, 0, 0, 0, + 0, 0, 0, 0xf2, 0xaa, 0x29, 0x02, + 0x04 } } }; +#endif + +#if defined(CONFIG_NET_IPV4) +/* Interface 1 addresses */ +static struct in_addr my_addr2 = { { { 192, 0, 2, 1 } } }; +#endif + +static struct net_if *iface1; + +static bool test_failed; +static bool test_started; +static bool timeout_query; +static struct k_sem wait_data; +static struct k_sem wait_data2; +static uint16_t current_dns_id; +static struct dns_addrinfo addrinfo; + +/* this must be higher that the DNS_TIMEOUT */ +#define WAIT_TIME (DNS_TIMEOUT + 300) + +struct net_if_test { + uint8_t idx; + uint8_t mac_addr[sizeof(struct net_eth_addr)]; + struct net_linkaddr ll_addr; +}; + +static int net_iface_dev_init(struct device *dev) +{ + return 0; +} + +static uint8_t *net_iface_get_mac(struct device *dev) +{ + struct net_if_test *data = dev->driver_data; + + if (data->mac_addr[2] == 0x00) { + /* 00-00-5E-00-53-xx Documentation RFC 7042 */ + data->mac_addr[0] = 0x00; + data->mac_addr[1] = 0x00; + data->mac_addr[2] = 0x5E; + data->mac_addr[3] = 0x00; + data->mac_addr[4] = 0x53; + data->mac_addr[5] = sys_rand32_get(); + } + + data->ll_addr.addr = data->mac_addr; + data->ll_addr.len = 6; + + return data->mac_addr; +} + +static void net_iface_init(struct net_if *iface) +{ + uint8_t *mac = net_iface_get_mac(net_if_get_device(iface)); + + net_if_set_link_addr(iface, mac, sizeof(struct net_eth_addr), + NET_LINK_ETHERNET); +} + +static inline int get_slot_by_id(struct dns_resolve_context *ctx, + uint16_t dns_id) +{ + int i; + + for (i = 0; i < CONFIG_DNS_NUM_CONCUR_QUERIES; i++) { + if (ctx->queries[i].cb && ctx->queries[i].id == dns_id) { + return i; + } + } + + return -1; +} + +static int sender_iface(struct net_if *iface, struct net_buf *buf) +{ + if (!buf->frags) { + DBG("No data to send!\n"); + return -ENODATA; + } + + if (!timeout_query) { + struct net_if_test *data = iface->dev->driver_data; + struct dns_resolve_context *ctx; + int slot; + + DBG("Sending at iface %d %p\n", net_if_get_by_iface(iface), + iface); + + if (net_nbuf_iface(buf) != iface) { + DBG("Invalid interface %p, expecting %p\n", + net_nbuf_iface(buf), iface); + test_failed = true; + } + + if (net_if_get_by_iface(iface) != data->idx) { + DBG("Invalid interface %d index, expecting %d\n", + data->idx, net_if_get_by_iface(iface)); + test_failed = true; + } + + ctx = dns_resolve_get_default(); + + slot = get_slot_by_id(ctx, current_dns_id); + if (slot < 0) { + DBG("Skipping this query dns id %u\n", current_dns_id); + goto out; + } + + /* We need to cancel the query manually so that we + * will not get a timeout. + */ + k_delayed_work_cancel(&ctx->queries[slot].timer); + + DBG("Calling cb %p with user data %p\n", + ctx->queries[slot].cb, + ctx->queries[slot].user_data); + + ctx->queries[slot].cb(DNS_EAI_INPROGRESS, + &addrinfo, + ctx->queries[slot].user_data); + ctx->queries[slot].cb(DNS_EAI_ALLDONE, + NULL, + ctx->queries[slot].user_data); + + ctx->queries[slot].cb = NULL; + } + +out: + net_nbuf_unref(buf); + + return 0; +} + +struct net_if_test net_iface1_data; + +static struct net_if_api net_iface_api = { + .init = net_iface_init, + .send = sender_iface, +}; + +#define _ETH_L2_LAYER DUMMY_L2 +#define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(DUMMY_L2) + +NET_DEVICE_INIT_INSTANCE(net_iface1_test, + "iface1", + iface1, + net_iface_dev_init, + &net_iface1_data, + NULL, + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, + &net_iface_api, + _ETH_L2_LAYER, + _ETH_L2_CTX_TYPE, + 127); + +static void test_init(void) +{ + struct net_if_addr *ifaddr; + + /* The semaphore is there to wait the data to be received. */ + k_sem_init(&wait_data, 0, UINT_MAX); + k_sem_init(&wait_data2, 0, UINT_MAX); + + iface1 = net_if_get_by_index(0); + + ((struct net_if_test *)iface1->dev->driver_data)->idx = 0; + +#if defined(CONFIG_NET_IPV6) + ifaddr = net_if_ipv6_addr_add(iface1, &my_addr1, + NET_ADDR_MANUAL, 0); + if (!ifaddr) { + DBG("Cannot add IPv6 address %s\n", + net_sprint_ipv6_addr(&my_addr1)); + assert_not_null(ifaddr, "addr1"); + } + + /* For testing purposes we need to set the adddresses preferred */ + ifaddr->addr_state = NET_ADDR_PREFERRED; + + ifaddr = net_if_ipv6_addr_add(iface1, &ll_addr, + NET_ADDR_MANUAL, 0); + if (!ifaddr) { + DBG("Cannot add IPv6 address %s\n", + net_sprint_ipv6_addr(&ll_addr)); + assert_not_null(ifaddr, "ll_addr"); + } + + ifaddr->addr_state = NET_ADDR_PREFERRED; +#endif + +#if defined(CONFIG_NET_IPV4) + ifaddr = net_if_ipv4_addr_add(iface1, &my_addr2, + NET_ADDR_MANUAL, 0); + if (!ifaddr) { + DBG("Cannot add IPv4 address %s\n", + net_sprint_ipv4_addr(&my_addr2)); + assert_not_null(ifaddr, "addr2"); + } + + ifaddr->addr_state = NET_ADDR_PREFERRED; +#endif + + net_if_up(iface1); + + /* The interface might receive data which might fail the checks + * in the iface sending function, so we need to reset the failure + * flag. + */ + test_failed = false; + + test_started = true; +} + +void dns_result_cb_dummy(enum dns_resolve_status status, + struct dns_addrinfo *info, + void *user_data) +{ + return; +} + +static void dns_query_invalid_timeout(void) +{ + int ret; + + ret = dns_get_addr_info(NAME6, + DNS_QUERY_TYPE_AAAA, + NULL, + dns_result_cb_dummy, + NULL, + K_NO_WAIT); + assert_equal(ret, -EINVAL, "Wrong return code for timeout"); +} + +static void dns_query_invalid_context(void) +{ + int ret; + + ret = dns_resolve_name(NULL, + NAME6, + DNS_QUERY_TYPE_AAAA, + NULL, + dns_result_cb_dummy, + NULL, + DNS_TIMEOUT); + assert_equal(ret, -EINVAL, "Wrong return code for context"); +} + +static void dns_query_invalid_callback(void) +{ + int ret; + + ret = dns_get_addr_info(NAME6, + DNS_QUERY_TYPE_AAAA, + NULL, + NULL, + NULL, + DNS_TIMEOUT); + assert_equal(ret, -EINVAL, "Wrong return code for callback"); +} + +static void dns_query_invalid_query(void) +{ + int ret; + + ret = dns_get_addr_info(NULL, + DNS_QUERY_TYPE_AAAA, + NULL, + dns_result_cb_dummy, + NULL, + DNS_TIMEOUT); + assert_equal(ret, -EINVAL, "Wrong return code for query"); +} + +void dns_result_cb_timeout(enum dns_resolve_status status, + struct dns_addrinfo *info, + void *user_data) +{ + int expected_status = POINTER_TO_INT(user_data); + + if (expected_status != status) { + DBG("Result status %d\n", status); + DBG("Expected status %d\n", expected_status); + + assert_equal(expected_status, status, "Invalid status"); + } + + k_sem_give(&wait_data); +} + +static void dns_query_server_count(void) +{ + struct dns_resolve_context *ctx = dns_resolve_get_default(); + int i, count = 0; + + for (i = 0; i < CONFIG_DNS_RESOLVER_MAX_SERVERS; i++) { + if (!ctx->is_used) { + continue; + } + + if (!ctx->servers[i].net_ctx) { + continue; + } + + count++; + } + + assert_equal(count, CONFIG_DNS_RESOLVER_MAX_SERVERS, + "Invalid number of servers"); +} + +static void dns_query_ipv4_server_count(void) +{ + struct dns_resolve_context *ctx = dns_resolve_get_default(); + int i, count = 0, port = 0; + + for (i = 0; i < CONFIG_DNS_RESOLVER_MAX_SERVERS; i++) { + if (!ctx->is_used) { + continue; + } + + if (!ctx->servers[i].net_ctx) { + continue; + } + + if (ctx->servers[i].dns_server.family == AF_INET) { + count++; + } + + if (net_sin(&ctx->servers[i].dns_server)->sin_port == + ntohs(53)) { + port++; + } + } + + assert_equal(count, 2, "Invalid number of IPv4 servers"); + assert_equal(port, 2, "Invalid number of IPv4 servers with port 53"); +} + +static void dns_query_ipv6_server_count(void) +{ + struct dns_resolve_context *ctx = dns_resolve_get_default(); + int i, count = 0, port = 0; + + for (i = 0; i < CONFIG_DNS_RESOLVER_MAX_SERVERS; i++) { + if (!ctx->is_used) { + continue; + } + + if (!ctx->servers[i].net_ctx) { + continue; + } + + if (ctx->servers[i].dns_server.family == AF_INET6) { + count++; + } + + if (net_sin6(&ctx->servers[i].dns_server)->sin6_port == + ntohs(53)) { + port++; + } + } + + assert_equal(count, 2, "Invalid number of IPv6 servers"); + assert_equal(port, 2, "Invalid number of IPv6 servers with port 53"); +} + +static void dns_query_too_many(void) +{ + int expected_status = DNS_EAI_CANCELED; + int ret; + + timeout_query = true; + + ret = dns_get_addr_info(NAME4, + DNS_QUERY_TYPE_A, + NULL, + dns_result_cb_timeout, + INT_TO_POINTER(expected_status), + DNS_TIMEOUT); + assert_equal(ret, 0, "Cannot create IPv4 query"); + + ret = dns_get_addr_info(NAME4, + DNS_QUERY_TYPE_A, + NULL, + dns_result_cb_dummy, + INT_TO_POINTER(expected_status), + DNS_TIMEOUT); + assert_equal(ret, -EAGAIN, "Should have run out of space"); + + if (k_sem_take(&wait_data, WAIT_TIME)) { + assert_true(false, "Timeout while waiting data"); + } + + timeout_query = false; +} + +static void dns_query_ipv4_timeout(void) +{ + int expected_status = DNS_EAI_CANCELED; + int ret; + + timeout_query = true; + + ret = dns_get_addr_info(NAME4, + DNS_QUERY_TYPE_A, + NULL, + dns_result_cb_timeout, + INT_TO_POINTER(expected_status), + DNS_TIMEOUT); + assert_equal(ret, 0, "Cannot create IPv4 query"); + + if (k_sem_take(&wait_data, WAIT_TIME)) { + assert_true(false, "Timeout while waiting data"); + } + + timeout_query = false; +} + +static void dns_query_ipv6_timeout(void) +{ + int expected_status = DNS_EAI_CANCELED; + int ret; + + timeout_query = true; + + ret = dns_get_addr_info(NAME6, + DNS_QUERY_TYPE_AAAA, + NULL, + dns_result_cb_timeout, + INT_TO_POINTER(expected_status), + DNS_TIMEOUT); + assert_equal(ret, 0, "Cannot create IPv6 query"); + + if (k_sem_take(&wait_data, WAIT_TIME)) { + assert_true(false, "Timeout while waiting data"); + } + + timeout_query = false; +} + +static void verify_cancelled(void) +{ + struct dns_resolve_context *ctx = dns_resolve_get_default(); + int i, count = 0, timer_not_stopped = 0; + + for (i = 0; i < CONFIG_DNS_NUM_CONCUR_QUERIES; i++) { + if (ctx->queries[i].cb) { + count++; + } + + if (k_delayed_work_remaining_get(&ctx->queries[i].timer) > 0) { + timer_not_stopped++; + } + } + + assert_equal(count, 0, "Not all pending queries vere cancelled"); + assert_equal(timer_not_stopped, 0, "Not all timers vere cancelled"); +} + +static void dns_query_ipv4_cancel(void) +{ + int expected_status = DNS_EAI_CANCELED; + uint16_t dns_id; + int ret; + + timeout_query = true; + + ret = dns_get_addr_info(NAME4, + DNS_QUERY_TYPE_A, + &dns_id, + dns_result_cb_timeout, + INT_TO_POINTER(expected_status), + DNS_TIMEOUT); + assert_equal(ret, 0, "Cannot create IPv4 query"); + + ret = dns_cancel_addr_info(dns_id); + assert_equal(ret, 0, "Cannot cancel IPv4 query"); + + if (k_sem_take(&wait_data, WAIT_TIME)) { + assert_true(false, "Timeout while waiting data"); + } + + verify_cancelled(); +} + +static void dns_query_ipv6_cancel(void) +{ + int expected_status = DNS_EAI_CANCELED; + uint16_t dns_id; + int ret; + + timeout_query = true; + + ret = dns_get_addr_info(NAME6, + DNS_QUERY_TYPE_AAAA, + &dns_id, + dns_result_cb_timeout, + INT_TO_POINTER(expected_status), + DNS_TIMEOUT); + assert_equal(ret, 0, "Cannot create IPv6 query"); + + ret = dns_cancel_addr_info(dns_id); + assert_equal(ret, 0, "Cannot cancel IPv6 query"); + + if (k_sem_take(&wait_data, WAIT_TIME)) { + assert_true(false, "Timeout while waiting data"); + } + + verify_cancelled(); +} + +struct expected_status { + int status1; + int status2; + const char *caller; +}; + +void dns_result_cb(enum dns_resolve_status status, + struct dns_addrinfo *info, + void *user_data) +{ + struct expected_status *expected = user_data; + + if (status != expected->status1 && status != expected->status2) { + DBG("Result status %d\n", status); + DBG("Expected status1 %d\n", expected->status1); + DBG("Expected status2 %d\n", expected->status2); + DBG("Caller %s\n", expected->caller); + + assert_true(false, "Invalid status"); + } + + k_sem_give(&wait_data2); +} + +static void dns_query_ipv4(void) +{ + struct expected_status status = { + .status1 = DNS_EAI_INPROGRESS, + .status2 = DNS_EAI_ALLDONE, + .caller = __func__, + }; + int ret; + + timeout_query = false; + + ret = dns_get_addr_info(NAME4, + DNS_QUERY_TYPE_A, + ¤t_dns_id, + dns_result_cb, + &status, + DNS_TIMEOUT); + assert_equal(ret, 0, "Cannot create IPv4 query"); + + DBG("Query id %u\n", current_dns_id); + + k_yield(); /* mandatory so that net_if send func gets to run */ + + if (k_sem_take(&wait_data2, WAIT_TIME)) { + assert_true(false, "Timeout while waiting data"); + } +} + +static void dns_query_ipv6(void) +{ + struct expected_status status = { + .status1 = DNS_EAI_INPROGRESS, + .status2 = DNS_EAI_ALLDONE, + .caller = __func__, + }; + int ret; + + timeout_query = false; + + ret = dns_get_addr_info(NAME6, + DNS_QUERY_TYPE_AAAA, + ¤t_dns_id, + dns_result_cb, + &status, + DNS_TIMEOUT); + assert_equal(ret, 0, "Cannot create IPv6 query"); + + DBG("Query id %u\n", current_dns_id); + + k_yield(); /* mandatory so that net_if send func gets to run */ + + if (k_sem_take(&wait_data2, WAIT_TIME)) { + assert_true(false, "Timeout while waiting data"); + } +} + +void test_main(void) +{ + ztest_test_suite(dns_tests, + ztest_unit_test(test_init), + ztest_unit_test(dns_query_invalid_timeout), + ztest_unit_test(dns_query_invalid_context), + ztest_unit_test(dns_query_invalid_callback), + ztest_unit_test(dns_query_invalid_query), + ztest_unit_test(dns_query_too_many), + ztest_unit_test(dns_query_server_count), + ztest_unit_test(dns_query_ipv4_server_count), + ztest_unit_test(dns_query_ipv6_server_count), + ztest_unit_test(dns_query_ipv4_timeout), + ztest_unit_test(dns_query_ipv6_timeout), + ztest_unit_test(dns_query_ipv4_cancel), + ztest_unit_test(dns_query_ipv6_cancel), + ztest_unit_test(dns_query_ipv4), + ztest_unit_test(dns_query_ipv6)); + + ztest_run_test_suite(dns_tests); +} diff --git a/tests/net/lib/dns_resolve/testcase.ini b/tests/net/lib/dns_resolve/testcase.ini new file mode 100644 index 00000000000..776b16b9393 --- /dev/null +++ b/tests/net/lib/dns_resolve/testcase.ini @@ -0,0 +1,6 @@ +[test] +tags = dns net +build_only = false +platform_whitelist = qemu_x86 +timeout = 10800 +slow = True