| /* main.c - Application main entry point */ |
| |
| /* |
| * Copyright (c) 2018 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define NET_LOG_LEVEL CONFIG_NET_TC_LOG_LEVEL |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(net_test, NET_LOG_LEVEL); |
| |
| #include <zephyr/types.h> |
| #include <stdbool.h> |
| #include <stddef.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <zephyr/sys/printk.h> |
| #include <zephyr/linker/sections.h> |
| #include <zephyr/random/random.h> |
| |
| #include <zephyr/ztest.h> |
| |
| #include <zephyr/net/ethernet.h> |
| #include <zephyr/net/dummy.h> |
| #include <zephyr/net/buf.h> |
| #include <zephyr/net/net_ip.h> |
| #include <zephyr/net/net_l2.h> |
| #include <zephyr/net/udp.h> |
| |
| #include "ipv6.h" |
| |
| #define NET_LOG_ENABLED 1 |
| #include "net_private.h" |
| |
| #if NET_LOG_LEVEL >= LOG_LEVEL_DBG |
| #define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__) |
| #else |
| #define DBG(fmt, ...) |
| #endif |
| |
| /* make this large enough so that we do not overflow the sent pkt array */ |
| #define MAX_PKT_TO_SEND 4 |
| #define MAX_PKT_TO_RECV 4 |
| |
| #define MAX_PRIORITIES 8 |
| #define MAX_TC 8 |
| |
| static enum net_priority send_priorities[MAX_TC][MAX_PKT_TO_SEND]; |
| static enum net_priority recv_priorities[MAX_TC][MAX_PKT_TO_RECV]; |
| |
| static enum net_priority tx_tc2prio[NET_TC_TX_COUNT]; |
| static enum net_priority rx_tc2prio[NET_TC_RX_COUNT]; |
| |
| #define TEST_PORT 9999 |
| |
| static const char *test_data = "Test data to be sent"; |
| |
| /* 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 } } }; |
| |
| /* Interface 2 addresses */ |
| static struct in6_addr my_addr2 = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; |
| |
| /* Interface 3 addresses */ |
| static struct in6_addr my_addr3 = { { { 0x20, 0x01, 0x0d, 0xb8, 2, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; |
| |
| /* Destination address for test packets */ |
| static struct in6_addr dst_addr = { { { 0x20, 0x01, 0x0d, 0xb8, 9, 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 } } }; |
| |
| static struct sockaddr_in6 dst_addr6 = { |
| .sin6_family = AF_INET6, |
| .sin6_port = htons(TEST_PORT), |
| }; |
| |
| static struct { |
| struct net_context *ctx; |
| } net_ctxs_tx[NET_TC_COUNT]; |
| |
| static struct { |
| struct net_context *ctx; |
| } net_ctxs_rx[NET_TC_COUNT]; |
| |
| static bool test_started; |
| static bool test_failed; |
| static bool start_receiving; |
| static bool recv_cb_called; |
| static struct k_sem wait_data; |
| |
| #define WAIT_TIME K_SECONDS(1) |
| |
| struct eth_context { |
| struct net_if *iface; |
| uint8_t mac_addr[6]; |
| |
| uint16_t expecting_tag; |
| }; |
| |
| static struct eth_context eth_context; |
| |
| static void eth_iface_init(struct net_if *iface) |
| { |
| const struct device *dev = net_if_get_device(iface); |
| struct eth_context *context = dev->data; |
| |
| net_if_set_link_addr(iface, context->mac_addr, |
| sizeof(context->mac_addr), |
| NET_LINK_ETHERNET); |
| } |
| |
| static bool check_higher_priority_pkt_sent(int tc, struct net_pkt *pkt) |
| { |
| /* If we have sent any higher priority packets, then |
| * this test fails as those packets should have been |
| * sent before this one. |
| */ |
| int j, k; |
| |
| for (j = tc + 1; j < MAX_TC; j++) { |
| for (k = 0; k < MAX_PKT_TO_SEND; k++) { |
| if (send_priorities[j][k]) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| static bool check_higher_priority_pkt_recv(int tc, struct net_pkt *pkt) |
| { |
| /* If we have received any higher priority packets, then |
| * this test fails as those packets should have been |
| * received before this one. |
| */ |
| int j, k; |
| |
| for (j = tc + 1; j < MAX_TC; j++) { |
| for (k = 0; k < MAX_PKT_TO_SEND; k++) { |
| if (recv_priorities[j][k]) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /* The eth_tx() will handle both sent packets or and it will also |
| * simulate the receiving of the packets. |
| */ |
| static int eth_tx(const struct device *dev, struct net_pkt *pkt) |
| { |
| if (!pkt->buffer) { |
| DBG("No data to send!\n"); |
| return -ENODATA; |
| } |
| |
| if (start_receiving) { |
| struct in6_addr addr; |
| struct net_udp_hdr hdr, *udp_hdr; |
| uint16_t port; |
| |
| DBG("Packet %p received\n", pkt); |
| |
| /* Swap IP src and destination address so that we can receive |
| * the packet and the stack will not reject it. |
| */ |
| net_ipv6_addr_copy_raw((uint8_t *)&addr, NET_IPV6_HDR(pkt)->src); |
| net_ipv6_addr_copy_raw(NET_IPV6_HDR(pkt)->src, |
| NET_IPV6_HDR(pkt)->dst); |
| net_ipv6_addr_copy_raw(NET_IPV6_HDR(pkt)->dst, (uint8_t *)&addr); |
| |
| udp_hdr = net_udp_get_hdr(pkt, &hdr); |
| zassert_not_null(udp_hdr, "UDP header missing"); |
| |
| port = udp_hdr->src_port; |
| udp_hdr->src_port = udp_hdr->dst_port; |
| udp_hdr->dst_port = port; |
| |
| if (net_recv_data(net_pkt_iface(pkt), |
| net_pkt_clone(pkt, K_NO_WAIT)) < 0) { |
| test_failed = true; |
| zassert_true(false, "Packet %p receive failed\n", pkt); |
| } |
| |
| return 0; |
| } |
| |
| if (test_started) { |
| #if NET_LOG_LEVEL >= LOG_LEVEL_DBG |
| k_tid_t thread = k_current_get(); |
| #endif |
| int i, prio, ret; |
| |
| prio = net_pkt_priority(pkt); |
| |
| for (i = 0; i < MAX_PKT_TO_SEND; i++) { |
| ret = check_higher_priority_pkt_sent( |
| net_tx_priority2tc(prio), pkt); |
| if (ret) { |
| DBG("Current thread priority %d " |
| "pkt %p prio %d tc %d\n", |
| k_thread_priority_get(thread), |
| pkt, prio, net_tx_priority2tc(prio)); |
| |
| test_failed = true; |
| zassert_false(test_failed, |
| "Invalid priority sent %d TC %d," |
| " expecting %d (pkt %p)\n", |
| prio, |
| net_tx_priority2tc(prio), |
| send_priorities[net_tx_priority2tc(prio)][i], |
| pkt); |
| goto fail; |
| } |
| |
| send_priorities[net_tx_priority2tc(prio)][i] = 0; |
| } |
| |
| DBG("Received pkt %p from TC %c (thread prio %d)\n", pkt, |
| *(pkt->frags->data + |
| sizeof(struct net_ipv6_hdr) + |
| sizeof(struct net_udp_hdr)), |
| k_thread_priority_get(thread)); |
| |
| k_sem_give(&wait_data); |
| } |
| |
| fail: |
| return 0; |
| } |
| |
| static struct dummy_api api_funcs = { |
| .iface_api.init = eth_iface_init, |
| .send = eth_tx, |
| }; |
| |
| static void generate_mac(uint8_t *mac_addr) |
| { |
| /* 00-00-5E-00-53-xx Documentation RFC 7042 */ |
| mac_addr[0] = 0x00; |
| mac_addr[1] = 0x00; |
| mac_addr[2] = 0x5E; |
| mac_addr[3] = 0x00; |
| mac_addr[4] = 0x53; |
| mac_addr[5] = sys_rand32_get(); |
| } |
| |
| static int eth_init(const struct device *dev) |
| { |
| struct eth_context *context = dev->data; |
| |
| generate_mac(context->mac_addr); |
| |
| return 0; |
| } |
| |
| /* Create one ethernet interface that does not have VLAN support. This |
| * is quite unlikely that this would be done in real life but for testing |
| * purposes create it here. |
| */ |
| NET_DEVICE_INIT(eth_test, "eth_test", eth_init, NULL, |
| ð_context, NULL, CONFIG_ETH_INIT_PRIORITY, &api_funcs, |
| DUMMY_L2, NET_L2_GET_CTX_TYPE(DUMMY_L2), |
| NET_ETH_MTU); |
| |
| static void address_setup(void) |
| { |
| struct net_if_addr *ifaddr; |
| struct net_if *iface1; |
| |
| iface1 = net_if_get_first_by_type(&NET_L2_GET_NAME(DUMMY)); |
| |
| zassert_not_null(iface1, "Interface 1"); |
| |
| 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)); |
| zassert_not_null(ifaddr, "addr1"); |
| } |
| |
| /* For testing purposes we need to set the addresses 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)); |
| zassert_not_null(ifaddr, "ll_addr"); |
| } |
| |
| ifaddr->addr_state = NET_ADDR_PREFERRED; |
| |
| ifaddr = net_if_ipv6_addr_add(iface1, &my_addr2, |
| NET_ADDR_MANUAL, 0); |
| if (!ifaddr) { |
| DBG("Cannot add IPv6 address %s\n", |
| net_sprint_ipv6_addr(&my_addr2)); |
| zassert_not_null(ifaddr, "addr2"); |
| } |
| |
| ifaddr->addr_state = NET_ADDR_PREFERRED; |
| |
| ifaddr = net_if_ipv6_addr_add(iface1, &my_addr3, |
| NET_ADDR_MANUAL, 0); |
| if (!ifaddr) { |
| DBG("Cannot add IPv6 address %s\n", |
| net_sprint_ipv6_addr(&my_addr3)); |
| zassert_not_null(ifaddr, "addr3"); |
| } |
| |
| 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; |
| } |
| |
| static void priority_setup(void) |
| { |
| int i; |
| |
| for (i = 0; i < MAX_PRIORITIES; i++) { |
| tx_tc2prio[net_tx_priority2tc(i)] = i; |
| rx_tc2prio[net_rx_priority2tc(i)] = i; |
| } |
| } |
| |
| #if defined(CONFIG_NET_IPV6_NBR_CACHE) |
| static bool add_neighbor(struct net_if *iface, struct in6_addr *addr) |
| { |
| struct net_linkaddr_storage llstorage; |
| struct net_linkaddr lladdr; |
| struct net_nbr *nbr; |
| |
| llstorage.addr[0] = 0x01; |
| llstorage.addr[1] = 0x02; |
| llstorage.addr[2] = 0x33; |
| llstorage.addr[3] = 0x44; |
| llstorage.addr[4] = 0x05; |
| llstorage.addr[5] = 0x06; |
| |
| lladdr.len = 6U; |
| lladdr.addr = llstorage.addr; |
| lladdr.type = NET_LINK_ETHERNET; |
| |
| nbr = net_ipv6_nbr_add(iface, addr, &lladdr, false, |
| NET_IPV6_NBR_STATE_REACHABLE); |
| if (!nbr) { |
| DBG("Cannot add dst %s to neighbor cache\n", |
| net_sprint_ipv6_addr(addr)); |
| return false; |
| } |
| |
| return true; |
| } |
| #else |
| #define add_neighbor(iface, addr) true |
| #endif /* CONFIG_NET_IPV6_NBR_CACHE */ |
| |
| static void setup_net_context(struct net_context **ctx) |
| { |
| struct sockaddr_in6 src_addr6 = { |
| .sin6_family = AF_INET6, |
| .sin6_port = 0, |
| }; |
| int ret; |
| struct net_if *iface1; |
| |
| iface1 = net_if_get_first_by_type(&NET_L2_GET_NAME(DUMMY)); |
| |
| ret = net_context_get(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, ctx); |
| zassert_equal(ret, 0, "Create IPv6 UDP context %p failed (%d)\n", |
| *ctx, ret); |
| |
| memcpy(&src_addr6.sin6_addr, &my_addr1, sizeof(struct in6_addr)); |
| memcpy(&dst_addr6.sin6_addr, &dst_addr, sizeof(struct in6_addr)); |
| |
| ret = add_neighbor(iface1, &dst_addr); |
| zassert_true(ret, "Cannot add neighbor"); |
| |
| ret = net_context_bind(*ctx, (struct sockaddr *)&src_addr6, |
| sizeof(struct sockaddr_in6)); |
| zassert_equal(ret, 0, |
| "Context bind failure test failed (%d)\n", ret); |
| } |
| |
| static void test_traffic_class_general_setup(void) |
| { |
| address_setup(); |
| priority_setup(); |
| } |
| |
| static void test_traffic_class_setup_tx(void) |
| { |
| uint8_t priority; |
| int i, ret; |
| |
| for (i = 0; i < NET_TC_TX_COUNT; i++) { |
| |
| setup_net_context(&net_ctxs_tx[i].ctx); |
| |
| priority = tx_tc2prio[i]; |
| |
| ret = net_context_set_option(net_ctxs_tx[i].ctx, |
| NET_OPT_PRIORITY, |
| &priority, sizeof(priority)); |
| zassert_equal(ret, 0, |
| "Cannot set priority %d to ctx %p (%d)\n", |
| priority, net_ctxs_tx[i].ctx, ret); |
| } |
| |
| } |
| |
| static void test_traffic_class_setup_rx(void) |
| { |
| uint8_t priority; |
| int i, ret; |
| |
| for (i = 0; i < NET_TC_RX_COUNT; i++) { |
| |
| setup_net_context(&net_ctxs_rx[i].ctx); |
| |
| priority = rx_tc2prio[i]; |
| |
| ret = net_context_set_option(net_ctxs_rx[i].ctx, |
| NET_OPT_PRIORITY, |
| &priority, sizeof(priority)); |
| zassert_equal(ret, 0, |
| "Cannot set priority %d to ctx %p (%d)\n", |
| priority, net_ctxs_rx[i].ctx, ret); |
| } |
| } |
| |
| static void test_traffic_class_cleanup_tx(void) |
| { |
| int i; |
| |
| for (i = 0; i < NET_TC_TX_COUNT; i++) { |
| if (net_ctxs_tx[i].ctx) { |
| net_context_unref(net_ctxs_tx[i].ctx); |
| net_ctxs_tx[i].ctx = NULL; |
| } |
| } |
| } |
| |
| static void test_traffic_class_cleanup_rx(void) |
| { |
| int i; |
| |
| for (i = 0; i < NET_TC_RX_COUNT; i++) { |
| if (net_ctxs_rx[i].ctx) { |
| net_context_unref(net_ctxs_rx[i].ctx); |
| net_ctxs_rx[i].ctx = NULL; |
| } |
| } |
| } |
| |
| static void traffic_class_send_packets_with_prio(enum net_priority prio, |
| int pkt_count) |
| { |
| /* Start to send data to each queue and verify that the data |
| * is received in correct order. |
| */ |
| uint8_t data[128]; |
| int len, ret; |
| int tc = net_tx_priority2tc(prio); |
| |
| /* Convert num to ascii */ |
| data[0] = tc + 0x30; |
| len = strlen(test_data); |
| memcpy(data+1, test_data, strlen(test_data)); |
| |
| len += 1; |
| |
| test_started = true; |
| |
| DBG("Sending on TC %d priority %d\n", tc, prio); |
| |
| send_priorities[net_tx_priority2tc(prio)][pkt_count - 1] = prio + 1; |
| |
| ret = net_context_sendto(net_ctxs_tx[tc].ctx, data, len, |
| (struct sockaddr *)&dst_addr6, |
| sizeof(struct sockaddr_in6), |
| NULL, K_NO_WAIT, NULL); |
| zassert_true(ret > 0, "Send UDP pkt failed"); |
| } |
| |
| static void traffic_class_send_priority(enum net_priority prio, |
| int num_packets, |
| bool wait_for_packets) |
| { |
| int i; |
| |
| if (wait_for_packets) { |
| k_sem_init(&wait_data, MAX_PKT_TO_SEND, UINT_MAX); |
| } |
| |
| for (i = 0; i < num_packets; i++) { |
| traffic_class_send_packets_with_prio(prio, i + 1); |
| } |
| |
| if (wait_for_packets) { |
| if (k_sem_take(&wait_data, WAIT_TIME)) { |
| DBG("Timeout while waiting ok status\n"); |
| zassert_false(true, "Timeout"); |
| } |
| |
| /* This sleep is needed here so that the sending side |
| * can run properly. |
| */ |
| k_sleep(K_MSEC(1)); |
| } |
| } |
| |
| static void test_traffic_class_send_data_prio_bk(void) |
| { |
| /* Send number of packets with each priority and make sure |
| * they are sent properly. |
| */ |
| traffic_class_send_priority(NET_PRIORITY_BK, MAX_PKT_TO_SEND, true); |
| } |
| |
| static void test_traffic_class_send_data_prio_be(void) |
| { |
| traffic_class_send_priority(NET_PRIORITY_BE, MAX_PKT_TO_SEND, true); |
| } |
| |
| static void test_traffic_class_send_data_prio_ee(void) |
| { |
| traffic_class_send_priority(NET_PRIORITY_EE, MAX_PKT_TO_SEND, true); |
| } |
| |
| static void test_traffic_class_send_data_prio_ca(void) |
| { |
| traffic_class_send_priority(NET_PRIORITY_CA, MAX_PKT_TO_SEND, true); |
| } |
| |
| static void test_traffic_class_send_data_prio_vi(void) |
| { |
| traffic_class_send_priority(NET_PRIORITY_VI, MAX_PKT_TO_SEND, true); |
| } |
| |
| static void test_traffic_class_send_data_prio_vo(void) |
| { |
| traffic_class_send_priority(NET_PRIORITY_VO, MAX_PKT_TO_SEND, true); |
| } |
| |
| static void test_traffic_class_send_data_prio_ic(void) |
| { |
| traffic_class_send_priority(NET_PRIORITY_IC, MAX_PKT_TO_SEND, true); |
| } |
| |
| static void test_traffic_class_send_data_prio_nc(void) |
| { |
| traffic_class_send_priority(NET_PRIORITY_NC, MAX_PKT_TO_SEND, true); |
| } |
| |
| static void test_traffic_class_send_data_mix(void) |
| { |
| /* Start to send data to each queue and verify that the data |
| * is received in correct order. |
| */ |
| int total_packets = 0; |
| |
| (void)memset(send_priorities, 0, sizeof(send_priorities)); |
| |
| traffic_class_send_priority(NET_PRIORITY_BK, MAX_PKT_TO_SEND, false); |
| total_packets += MAX_PKT_TO_SEND; |
| |
| traffic_class_send_priority(NET_PRIORITY_BE, MAX_PKT_TO_SEND, false); |
| total_packets += MAX_PKT_TO_SEND; |
| |
| /* The semaphore is released as many times as we have sent packets */ |
| k_sem_init(&wait_data, total_packets, UINT_MAX); |
| |
| if (k_sem_take(&wait_data, WAIT_TIME)) { |
| DBG("Timeout while waiting ok status\n"); |
| zassert_false(true, "Timeout"); |
| } |
| |
| zassert_false(test_failed, "Traffic class verification failed."); |
| } |
| |
| static void test_traffic_class_send_data_mix_all_1(void) |
| { |
| int total_packets = 0; |
| |
| (void)memset(send_priorities, 0, sizeof(send_priorities)); |
| |
| traffic_class_send_priority(NET_PRIORITY_BK, MAX_PKT_TO_SEND, false); |
| total_packets += MAX_PKT_TO_SEND; |
| |
| traffic_class_send_priority(NET_PRIORITY_BE, MAX_PKT_TO_SEND, false); |
| total_packets += MAX_PKT_TO_SEND; |
| |
| traffic_class_send_priority(NET_PRIORITY_EE, MAX_PKT_TO_SEND, false); |
| total_packets += MAX_PKT_TO_SEND; |
| |
| traffic_class_send_priority(NET_PRIORITY_CA, MAX_PKT_TO_SEND, false); |
| total_packets += MAX_PKT_TO_SEND; |
| |
| traffic_class_send_priority(NET_PRIORITY_VI, MAX_PKT_TO_SEND, false); |
| total_packets += MAX_PKT_TO_SEND; |
| |
| traffic_class_send_priority(NET_PRIORITY_VO, MAX_PKT_TO_SEND, false); |
| total_packets += MAX_PKT_TO_SEND; |
| |
| traffic_class_send_priority(NET_PRIORITY_IC, MAX_PKT_TO_SEND, false); |
| total_packets += MAX_PKT_TO_SEND; |
| |
| traffic_class_send_priority(NET_PRIORITY_NC, MAX_PKT_TO_SEND, false); |
| total_packets += MAX_PKT_TO_SEND; |
| |
| /* The semaphore is released as many times as we have sent packets */ |
| k_sem_init(&wait_data, total_packets, UINT_MAX); |
| |
| if (k_sem_take(&wait_data, WAIT_TIME)) { |
| DBG("Timeout while waiting ok status\n"); |
| zassert_false(true, "Timeout"); |
| } |
| |
| zassert_false(test_failed, "Traffic class verification failed."); |
| } |
| |
| static void test_traffic_class_send_data_mix_all_2(void) |
| { |
| /* Start to send data to each queue and verify that the data |
| * is received in correct order. |
| */ |
| int total_packets = 0; |
| int i; |
| |
| (void)memset(send_priorities, 0, sizeof(send_priorities)); |
| |
| /* In this test send one packet for each queue instead of sending |
| * n packets to same queue at a time. |
| */ |
| for (i = 0; i < MAX_PKT_TO_SEND; i++) { |
| traffic_class_send_priority(NET_PRIORITY_BK, 1, false); |
| total_packets += 1; |
| |
| traffic_class_send_priority(NET_PRIORITY_BE, 1, false); |
| total_packets += 1; |
| |
| traffic_class_send_priority(NET_PRIORITY_EE, 1, false); |
| total_packets += 1; |
| |
| traffic_class_send_priority(NET_PRIORITY_CA, 1, false); |
| total_packets += 1; |
| |
| traffic_class_send_priority(NET_PRIORITY_VI, 1, false); |
| total_packets += 1; |
| |
| traffic_class_send_priority(NET_PRIORITY_VO, 1, false); |
| total_packets += 1; |
| |
| traffic_class_send_priority(NET_PRIORITY_IC, 1, false); |
| total_packets += 1; |
| |
| traffic_class_send_priority(NET_PRIORITY_NC, 1, false); |
| total_packets += 1; |
| } |
| |
| /* The semaphore is released as many times as we have sent packets */ |
| k_sem_init(&wait_data, total_packets, UINT_MAX); |
| |
| if (k_sem_take(&wait_data, WAIT_TIME)) { |
| DBG("Timeout while waiting ok status\n"); |
| zassert_false(true, "Timeout"); |
| } |
| |
| zassert_false(test_failed, "Traffic class verification failed."); |
| } |
| |
| static void recv_cb(struct net_context *context, |
| struct net_pkt *pkt, |
| union net_ip_header *ip_hdr, |
| union net_proto_header *proto_hdr, |
| int status, |
| void *user_data) |
| { |
| #if NET_LOG_LEVEL >= LOG_LEVEL_DBG |
| k_tid_t thread = k_current_get(); |
| #endif |
| int i, prio, ret; |
| |
| DBG("Data received in priority %d\n", k_thread_priority_get(thread)); |
| |
| prio = net_pkt_priority(pkt); |
| |
| for (i = 0; i < MAX_PKT_TO_RECV; i++) { |
| ret = check_higher_priority_pkt_recv(net_rx_priority2tc(prio), |
| pkt); |
| if (ret) { |
| DBG("Current thread priority %d " |
| "pkt %p prio %d tc %d\n", |
| k_thread_priority_get(thread), |
| pkt, prio, net_rx_priority2tc(prio)); |
| |
| test_failed = true; |
| zassert_false(test_failed, |
| "Invalid priority received %d TC %d," |
| " expecting %d (pkt %p)\n", |
| prio, |
| net_rx_priority2tc(prio), |
| recv_priorities[net_rx_priority2tc(prio)][i], |
| pkt); |
| goto fail; |
| } |
| |
| recv_priorities[net_rx_priority2tc(prio)][i] = 0; |
| } |
| |
| fail: |
| recv_cb_called = true; |
| k_sem_give(&wait_data); |
| |
| net_pkt_unref(pkt); |
| } |
| |
| static void test_traffic_class_setup_recv(void) |
| { |
| int ret, i; |
| |
| recv_cb_called = false; |
| |
| for (i = 0; i < NET_TC_RX_COUNT; i++) { |
| ret = net_context_recv(net_ctxs_rx[i].ctx, recv_cb, |
| K_NO_WAIT, NULL); |
| zassert_equal(ret, 0, |
| "[%d] Context recv UDP setup failed (%d)\n", |
| i, ret); |
| } |
| } |
| |
| static void traffic_class_recv_packets_with_prio(enum net_priority prio, |
| int pkt_count) |
| { |
| /* Start to receive data to each queue and verify that the data |
| * is received in correct order. |
| */ |
| uint8_t data[128]; |
| int len, ret; |
| int tc = net_rx_priority2tc(prio); |
| |
| const struct in6_addr *src_addr; |
| struct net_if_addr *ifaddr; |
| struct net_if *iface = NULL; |
| |
| /* Convert num to ascii */ |
| data[0] = tc + 0x30; |
| len = strlen(test_data); |
| memcpy(data+1, test_data, strlen(test_data)); |
| |
| len += 1; |
| |
| test_started = true; |
| start_receiving = true; |
| |
| DBG("Receiving on TC %d priority %d\n", tc, prio); |
| |
| recv_priorities[net_rx_priority2tc(prio)][pkt_count - 1] = prio + 1; |
| |
| src_addr = net_if_ipv6_select_src_addr(NULL, &dst_addr); |
| zassert_not_null(src_addr, "Cannot select source address"); |
| |
| ifaddr = net_if_ipv6_addr_lookup(src_addr, &iface); |
| zassert_not_null(ifaddr, "Cannot find source address"); |
| zassert_not_null(iface, "Interface not found"); |
| |
| /* We cannot use net_recv_data() here as the packet does not have |
| * UDP header. |
| */ |
| ret = net_context_sendto(net_ctxs_rx[tc].ctx, data, len, |
| (struct sockaddr *)&dst_addr6, |
| sizeof(struct sockaddr_in6), |
| NULL, K_NO_WAIT, NULL); |
| zassert_true(ret > 0, "Send UDP pkt failed"); |
| |
| /* Let the receiver to receive the packets */ |
| k_sleep(K_MSEC(1)); |
| } |
| |
| static void traffic_class_recv_priority(enum net_priority prio, |
| int num_packets, |
| bool wait_for_packets) |
| { |
| int i; |
| |
| if (wait_for_packets) { |
| k_sem_init(&wait_data, MAX_PKT_TO_RECV, UINT_MAX); |
| } |
| |
| for (i = 0; i < num_packets; i++) { |
| traffic_class_recv_packets_with_prio(prio, i + 1); |
| } |
| |
| if (wait_for_packets) { |
| if (k_sem_take(&wait_data, WAIT_TIME)) { |
| DBG("Timeout while waiting ok status\n"); |
| zassert_false(true, "Timeout"); |
| } |
| |
| /* This sleep is needed here so that the receiving side |
| * can run properly. |
| */ |
| k_sleep(K_MSEC(1)); |
| } |
| } |
| |
| static void test_traffic_class_recv_data_prio_bk(void) |
| { |
| /* Receive number of packets with each priority and make sure |
| * they are received properly. |
| */ |
| traffic_class_recv_priority(NET_PRIORITY_BK, MAX_PKT_TO_RECV, true); |
| |
| zassert_false(test_failed, "Traffic class verification failed."); |
| } |
| |
| static void test_traffic_class_recv_data_prio_be(void) |
| { |
| traffic_class_recv_priority(NET_PRIORITY_BE, MAX_PKT_TO_RECV, true); |
| } |
| |
| static void test_traffic_class_recv_data_prio_ee(void) |
| { |
| traffic_class_recv_priority(NET_PRIORITY_EE, MAX_PKT_TO_RECV, true); |
| } |
| |
| static void test_traffic_class_recv_data_prio_ca(void) |
| { |
| traffic_class_recv_priority(NET_PRIORITY_CA, MAX_PKT_TO_RECV, true); |
| } |
| |
| static void test_traffic_class_recv_data_prio_vi(void) |
| { |
| traffic_class_recv_priority(NET_PRIORITY_VI, MAX_PKT_TO_RECV, true); |
| } |
| |
| static void test_traffic_class_recv_data_prio_vo(void) |
| { |
| traffic_class_recv_priority(NET_PRIORITY_VO, MAX_PKT_TO_RECV, true); |
| } |
| |
| static void test_traffic_class_recv_data_prio_ic(void) |
| { |
| traffic_class_recv_priority(NET_PRIORITY_IC, MAX_PKT_TO_RECV, true); |
| } |
| |
| static void test_traffic_class_recv_data_prio_nc(void) |
| { |
| traffic_class_recv_priority(NET_PRIORITY_NC, MAX_PKT_TO_RECV, true); |
| } |
| |
| static void test_traffic_class_recv_data_mix(void) |
| { |
| /* Start to receive data to each queue and verify that the data |
| * is received in correct order. |
| */ |
| int total_packets = 0; |
| |
| (void)memset(recv_priorities, 0, sizeof(recv_priorities)); |
| |
| traffic_class_recv_priority(NET_PRIORITY_BK, MAX_PKT_TO_RECV, false); |
| total_packets += MAX_PKT_TO_RECV; |
| |
| traffic_class_recv_priority(NET_PRIORITY_BE, MAX_PKT_TO_RECV, false); |
| total_packets += MAX_PKT_TO_RECV; |
| |
| /* The semaphore is released as many times as we have sent packets */ |
| k_sem_init(&wait_data, total_packets, UINT_MAX); |
| |
| if (k_sem_take(&wait_data, WAIT_TIME)) { |
| DBG("Timeout while waiting ok status\n"); |
| zassert_false(true, "Timeout"); |
| } |
| |
| zassert_false(test_failed, "Traffic class verification failed."); |
| } |
| |
| static void test_traffic_class_recv_data_mix_all_1(void) |
| { |
| int total_packets = 0; |
| |
| (void)memset(recv_priorities, 0, sizeof(recv_priorities)); |
| |
| traffic_class_recv_priority(NET_PRIORITY_BK, MAX_PKT_TO_RECV, false); |
| total_packets += MAX_PKT_TO_RECV; |
| |
| traffic_class_recv_priority(NET_PRIORITY_BE, MAX_PKT_TO_RECV, false); |
| total_packets += MAX_PKT_TO_RECV; |
| |
| traffic_class_recv_priority(NET_PRIORITY_EE, MAX_PKT_TO_RECV, false); |
| total_packets += MAX_PKT_TO_RECV; |
| |
| traffic_class_recv_priority(NET_PRIORITY_CA, MAX_PKT_TO_RECV, false); |
| total_packets += MAX_PKT_TO_RECV; |
| |
| traffic_class_recv_priority(NET_PRIORITY_VI, MAX_PKT_TO_RECV, false); |
| total_packets += MAX_PKT_TO_RECV; |
| |
| traffic_class_recv_priority(NET_PRIORITY_VO, MAX_PKT_TO_RECV, false); |
| total_packets += MAX_PKT_TO_RECV; |
| |
| traffic_class_recv_priority(NET_PRIORITY_IC, MAX_PKT_TO_RECV, false); |
| total_packets += MAX_PKT_TO_RECV; |
| |
| traffic_class_recv_priority(NET_PRIORITY_NC, MAX_PKT_TO_RECV, false); |
| total_packets += MAX_PKT_TO_RECV; |
| |
| /* The semaphore is released as many times as we have sent packets */ |
| k_sem_init(&wait_data, total_packets, UINT_MAX); |
| |
| if (k_sem_take(&wait_data, WAIT_TIME)) { |
| DBG("Timeout while waiting ok status\n"); |
| zassert_false(true, "Timeout"); |
| } |
| |
| zassert_false(test_failed, "Traffic class verification failed."); |
| } |
| |
| static void test_traffic_class_recv_data_mix_all_2(void) |
| { |
| /* Start to receive data to each queue and verify that the data |
| * is received in correct order. |
| */ |
| int total_packets = 0; |
| int i; |
| |
| (void)memset(recv_priorities, 0, sizeof(recv_priorities)); |
| |
| /* In this test receive one packet for each queue instead of receiving |
| * n packets to same queue at a time. |
| */ |
| for (i = 0; i < MAX_PKT_TO_RECV; i++) { |
| traffic_class_recv_priority(NET_PRIORITY_BK, 1, false); |
| total_packets += 1; |
| |
| traffic_class_recv_priority(NET_PRIORITY_BE, 1, false); |
| total_packets += 1; |
| |
| traffic_class_recv_priority(NET_PRIORITY_EE, 1, false); |
| total_packets += 1; |
| |
| traffic_class_recv_priority(NET_PRIORITY_CA, 1, false); |
| total_packets += 1; |
| |
| traffic_class_recv_priority(NET_PRIORITY_VI, 1, false); |
| total_packets += 1; |
| |
| traffic_class_recv_priority(NET_PRIORITY_VO, 1, false); |
| total_packets += 1; |
| |
| traffic_class_recv_priority(NET_PRIORITY_IC, 1, false); |
| total_packets += 1; |
| |
| traffic_class_recv_priority(NET_PRIORITY_NC, 1, false); |
| total_packets += 1; |
| } |
| |
| /* The semaphore is released as many times as we have sent packets */ |
| k_sem_init(&wait_data, total_packets, UINT_MAX); |
| |
| if (k_sem_take(&wait_data, WAIT_TIME)) { |
| DBG("Timeout while waiting ok status\n"); |
| zassert_false(true, "Timeout"); |
| } |
| |
| zassert_false(test_failed, "Traffic class verification failed."); |
| } |
| |
| ZTEST(net_traffic_class, test_bk) |
| { |
| test_traffic_class_send_data_prio_bk(); |
| test_traffic_class_recv_data_prio_bk(); |
| } |
| |
| ZTEST(net_traffic_class, test_be) |
| { |
| test_traffic_class_send_data_prio_be(); |
| test_traffic_class_recv_data_prio_be(); |
| } |
| |
| ZTEST(net_traffic_class, test_ee) |
| { |
| test_traffic_class_send_data_prio_ee(); |
| test_traffic_class_recv_data_prio_ee(); |
| } |
| |
| ZTEST(net_traffic_class, test_ca) |
| { |
| test_traffic_class_send_data_prio_ca(); |
| test_traffic_class_recv_data_prio_ca(); |
| } |
| |
| ZTEST(net_traffic_class, test_vi) |
| { |
| test_traffic_class_send_data_prio_vi(); |
| test_traffic_class_recv_data_prio_vi(); |
| } |
| |
| ZTEST(net_traffic_class, test_vo) |
| { |
| test_traffic_class_send_data_prio_vo(); |
| test_traffic_class_recv_data_prio_vo(); |
| } |
| |
| ZTEST(net_traffic_class, test_ic) |
| { |
| test_traffic_class_send_data_prio_ic(); |
| test_traffic_class_recv_data_prio_ic(); |
| } |
| |
| ZTEST(net_traffic_class, test_nc) |
| { |
| test_traffic_class_send_data_prio_nc(); |
| test_traffic_class_recv_data_prio_nc(); |
| } |
| |
| ZTEST(net_traffic_class, test_mix) |
| { |
| test_traffic_class_send_data_mix(); |
| test_traffic_class_recv_data_mix(); |
| } |
| |
| ZTEST(net_traffic_class, test_mix_all_1) |
| { |
| test_traffic_class_send_data_mix_all_1(); |
| test_traffic_class_recv_data_mix_all_1(); |
| } |
| |
| ZTEST(net_traffic_class, test_mix_all_2) |
| { |
| test_traffic_class_send_data_mix_all_2(); |
| test_traffic_class_recv_data_mix_all_2(); |
| } |
| |
| static void run_before(void *dummy) |
| { |
| ARG_UNUSED(dummy); |
| test_traffic_class_general_setup(); |
| test_traffic_class_setup_tx(); |
| test_traffic_class_setup_rx(); |
| test_traffic_class_setup_recv(); |
| } |
| |
| static void run_after(void *dummy) |
| { |
| ARG_UNUSED(dummy); |
| test_traffic_class_cleanup_tx(); |
| test_traffic_class_cleanup_rx(); |
| } |
| |
| ZTEST_SUITE(net_traffic_class, NULL, NULL, run_before, run_after, NULL); |