blob: a6d9a3756d84592076bfe7bf16aa1f8034f2c909 [file] [log] [blame]
/*
* Copyright (c) 2025 The Zephyr Contributors.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/net/ethernet.h>
#include <zephyr/net/net_pkt_filter.h>
#include <posix_board_if.h>
LOG_MODULE_REGISTER(qos_ethernet);
#define NET_ETH_PTYPE_123 123
#define NET_ETH_PTYPE_124 124
#define NET_ETH_PTYPE_125 125
#define NET_ETH_PTYPE_126 126
#define NET_ETH_PTYPE_127 127
#define NET_ETH_PTYPE_128 128
#define NET_ETH_PTYPE_129 129
#define NET_ETH_PTYPE_130 130
#define SINGLE_RUN_DEADLINE K_MSEC(2000)
#define MTU 1500
enum service_type {
SERVICE_TYPE_COMMAND,
SERVICE_TYPE_ECHO,
};
struct net_if_fake_data {
uint8_t mac[sizeof(struct net_eth_addr)];
struct net_linkaddr ll_addr;
};
struct statistics {
unsigned int service_123_count;
unsigned int service_124_count;
unsigned int service_125_count;
unsigned int service_126_count;
unsigned int service_127_count;
unsigned int service_128_count;
unsigned int service_129_count;
unsigned int service_130_count;
unsigned int echo_123_count;
unsigned int echo_124_count;
unsigned int echo_125_count;
unsigned int echo_126_count;
unsigned int echo_127_count;
unsigned int echo_128_count;
unsigned int echo_129_count;
unsigned int echo_130_count;
};
static K_SEM_DEFINE(service_123_received, 0, UINT_MAX);
static K_SEM_DEFINE(service_124_received, 0, UINT_MAX);
static K_SEM_DEFINE(service_125_received, 0, UINT_MAX);
static K_SEM_DEFINE(service_126_received, 0, UINT_MAX);
static K_SEM_DEFINE(service_127_received, 0, UINT_MAX);
static K_SEM_DEFINE(service_128_received, 0, UINT_MAX);
static K_SEM_DEFINE(service_129_received, 0, UINT_MAX);
static K_SEM_DEFINE(service_130_received, 0, UINT_MAX);
static K_SEM_DEFINE(echo_123_send, 0, UINT_MAX);
static K_SEM_DEFINE(echo_124_send, 0, UINT_MAX);
static K_SEM_DEFINE(echo_125_send, 0, UINT_MAX);
static K_SEM_DEFINE(echo_126_send, 0, UINT_MAX);
static K_SEM_DEFINE(echo_127_send, 0, UINT_MAX);
static K_SEM_DEFINE(echo_128_send, 0, UINT_MAX);
static K_SEM_DEFINE(echo_129_send, 0, UINT_MAX);
static K_SEM_DEFINE(echo_130_send, 0, UINT_MAX);
static NPF_ETH_TYPE_MATCH(npf_123, NET_ETH_PTYPE_123);
static NPF_ETH_TYPE_MATCH(npf_124, NET_ETH_PTYPE_124);
static NPF_ETH_TYPE_MATCH(npf_125, NET_ETH_PTYPE_125);
static NPF_ETH_TYPE_MATCH(npf_126, NET_ETH_PTYPE_126);
static NPF_ETH_TYPE_MATCH(npf_127, NET_ETH_PTYPE_127);
static NPF_ETH_TYPE_MATCH(npf_128, NET_ETH_PTYPE_128);
static NPF_ETH_TYPE_MATCH(npf_129, NET_ETH_PTYPE_129);
static NPF_ETH_TYPE_MATCH(npf_130, NET_ETH_PTYPE_130);
static NPF_PRIORITY(priority_rule_123, NET_PRIORITY_BK, npf_123);
static NPF_PRIORITY(priority_rule_124, NET_PRIORITY_BE, npf_124);
static NPF_PRIORITY(priority_rule_125, NET_PRIORITY_EE, npf_125);
static NPF_PRIORITY(priority_rule_126, NET_PRIORITY_CA, npf_126);
static NPF_PRIORITY(priority_rule_127, NET_PRIORITY_VI, npf_127);
static NPF_PRIORITY(priority_rule_128, NET_PRIORITY_VO, npf_128);
static NPF_PRIORITY(priority_rule_129, NET_PRIORITY_IC, npf_129);
static NPF_PRIORITY(priority_rule_130, NET_PRIORITY_NC, npf_130);
static uint32_t simulated_work_time; /* data race ! */
int net_fake_dev_init(const struct device *dev)
{
return 0;
}
static void copy_mac_to(char destination[static 6])
{
/* 00-00-5E-00-53-xx Documentation RFC 7042 */
/* taken from arp test */
destination[0] = 0x00;
destination[1] = 0x00;
destination[2] = 0x5E;
destination[3] = 0x00;
destination[4] = 0x53;
destination[5] = 0x3B;
}
static void net_iface_init(struct net_if *iface)
{
const struct device *device = net_if_get_device(iface);
struct net_if_fake_data *context = device->data;
if (context->mac[2] == 0x00) {
copy_mac_to(context->mac);
}
net_if_set_link_addr(iface, context->mac, sizeof(context->mac), NET_LINK_ETHERNET);
}
static int net_if_fake_send(const struct device *dev, struct net_pkt *pkt)
{
LOG_INF("sending service %u, pkt %p", net_pkt_ll_proto_type(pkt), pkt);
posix_cpu_hold(simulated_work_time);
switch (net_pkt_ll_proto_type(pkt)) {
case NET_ETH_PTYPE_123:
k_sem_give(&echo_123_send);
break;
case NET_ETH_PTYPE_124:
k_sem_give(&echo_124_send);
break;
case NET_ETH_PTYPE_125:
k_sem_give(&echo_125_send);
break;
case NET_ETH_PTYPE_126:
k_sem_give(&echo_126_send);
break;
case NET_ETH_PTYPE_127:
k_sem_give(&echo_127_send);
break;
case NET_ETH_PTYPE_128:
k_sem_give(&echo_128_send);
break;
case NET_ETH_PTYPE_129:
k_sem_give(&echo_129_send);
break;
case NET_ETH_PTYPE_130:
k_sem_give(&echo_130_send);
break;
default: /* nothing to do */
break;
}
net_pkt_unref(pkt);
return 0;
}
static const struct ethernet_api net_if_api = {
.iface_api.init = net_iface_init,
.send = net_if_fake_send,
};
static struct net_if_fake_data context;
#define _ETH_L2_LAYER ETHERNET_L2
#define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(ETHERNET_L2)
NET_DEVICE_INIT(net_if_fake, "net_if_fake", net_fake_dev_init, NULL, &context, NULL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &net_if_api, _ETH_L2_LAYER, _ETH_L2_CTX_TYPE,
MTU);
static void try_recv_data(struct net_if *iface, uint16_t ptype, enum service_type service_type)
{
int res;
struct net_pkt *pkt = NULL;
char src[6] = {6, 7, 8, 9, 10, 11};
char dest[6];
copy_mac_to(dest);
pkt = net_pkt_rx_alloc_with_buffer(iface, MTU, AF_UNSPEC, 0, K_NO_WAIT);
if (pkt == NULL) {
return;
}
if (net_pkt_write(pkt, dest, ARRAY_SIZE(dest))) {
goto error;
}
if (net_pkt_write(pkt, src, ARRAY_SIZE(src))) {
goto error;
}
if (net_pkt_write_be16(pkt, ptype)) {
goto error;
}
if (net_pkt_write_u8(pkt, service_type)) {
goto error;
}
res = net_recv_data(net_pkt_iface(pkt), pkt);
if (res < 0) {
LOG_ERR("Failed to enqueue frame into RX queue: %d", res);
goto error;
}
return;
error:
net_pkt_unref(pkt);
}
struct statistics single_run_with_simulated_work(struct net_if *iface, uint32_t work)
{
k_timepoint_t deadline = sys_timepoint_calc(SINGLE_RUN_DEADLINE);
simulated_work_time = work;
k_sem_reset(&service_123_received);
k_sem_reset(&service_124_received);
k_sem_reset(&service_125_received);
k_sem_reset(&service_126_received);
k_sem_reset(&service_127_received);
k_sem_reset(&service_128_received);
k_sem_reset(&service_129_received);
k_sem_reset(&service_130_received);
k_sem_reset(&echo_123_send);
k_sem_reset(&echo_124_send);
k_sem_reset(&echo_125_send);
k_sem_reset(&echo_126_send);
k_sem_reset(&echo_127_send);
k_sem_reset(&echo_128_send);
k_sem_reset(&echo_129_send);
k_sem_reset(&echo_130_send);
while (!sys_timepoint_expired(deadline)) {
/*every tick try receive a packet of each type*/
try_recv_data(iface, NET_ETH_PTYPE_123, SERVICE_TYPE_ECHO);
try_recv_data(iface, NET_ETH_PTYPE_123, SERVICE_TYPE_COMMAND);
try_recv_data(iface, NET_ETH_PTYPE_124, SERVICE_TYPE_ECHO);
try_recv_data(iface, NET_ETH_PTYPE_124, SERVICE_TYPE_COMMAND);
try_recv_data(iface, NET_ETH_PTYPE_125, SERVICE_TYPE_ECHO);
try_recv_data(iface, NET_ETH_PTYPE_125, SERVICE_TYPE_COMMAND);
try_recv_data(iface, NET_ETH_PTYPE_126, SERVICE_TYPE_ECHO);
try_recv_data(iface, NET_ETH_PTYPE_126, SERVICE_TYPE_COMMAND);
try_recv_data(iface, NET_ETH_PTYPE_127, SERVICE_TYPE_ECHO);
try_recv_data(iface, NET_ETH_PTYPE_127, SERVICE_TYPE_COMMAND);
try_recv_data(iface, NET_ETH_PTYPE_128, SERVICE_TYPE_ECHO);
try_recv_data(iface, NET_ETH_PTYPE_128, SERVICE_TYPE_COMMAND);
try_recv_data(iface, NET_ETH_PTYPE_129, SERVICE_TYPE_ECHO);
try_recv_data(iface, NET_ETH_PTYPE_129, SERVICE_TYPE_COMMAND);
try_recv_data(iface, NET_ETH_PTYPE_130, SERVICE_TYPE_ECHO);
try_recv_data(iface, NET_ETH_PTYPE_130, SERVICE_TYPE_COMMAND);
k_sleep(K_TICKS(1));
}
return (struct statistics){
.service_123_count = k_sem_count_get(&service_123_received),
.service_124_count = k_sem_count_get(&service_124_received),
.service_125_count = k_sem_count_get(&service_125_received),
.service_126_count = k_sem_count_get(&service_126_received),
.service_127_count = k_sem_count_get(&service_127_received),
.service_128_count = k_sem_count_get(&service_128_received),
.service_129_count = k_sem_count_get(&service_129_received),
.service_130_count = k_sem_count_get(&service_130_received),
.echo_123_count = k_sem_count_get(&echo_123_send),
.echo_124_count = k_sem_count_get(&echo_124_send),
.echo_125_count = k_sem_count_get(&echo_125_send),
.echo_126_count = k_sem_count_get(&echo_126_send),
.echo_127_count = k_sem_count_get(&echo_127_send),
.echo_128_count = k_sem_count_get(&echo_128_send),
.echo_129_count = k_sem_count_get(&echo_129_send),
.echo_130_count = k_sem_count_get(&echo_130_send),
};
}
void print_result(const char *msg, size_t cnt, uint32_t simulated_work_times[static cnt],
struct statistics stats[static cnt])
{
LOG_INF("--- Statistics (%s) ---", msg);
LOG_INF("c (x) := command service for priority x (high means higher priority)");
LOG_INF("e (x) := echo service for priority x (high means higher priority)");
LOG_INF("+---------+------+------+------+------+------+------+------+------+------+------+-"
"-----+------+------+------+------+------+");
LOG_INF("| work us | c(7) | e(7) | c(6) | e(6) | c(5) | e(5) | c(4) | e(4) | c(3) | e(3) | "
"c(2) | e(2) | c(1) | e(1) | c(0) | e(0) |");
LOG_INF("+=========+======+======+======+======+======+======+======+======+======+======+="
"=====+======+======+======+======+======+");
for (size_t i = 0; i < cnt; ++i) {
LOG_INF("| %7u | %4d | %4d | %4d | %4d | %4d | %4d | %4d | %4d | %4d | %4d | %4d | "
"%4d | %4d | %4d | %4d | %4d |",
simulated_work_times[i], stats[i].service_130_count,
stats[i].echo_130_count, stats[i].service_129_count,
stats[i].echo_129_count, stats[i].service_128_count,
stats[i].echo_128_count, stats[i].service_127_count,
stats[i].echo_127_count, stats[i].service_126_count,
stats[i].echo_126_count, stats[i].service_125_count,
stats[i].echo_125_count, stats[i].service_124_count,
stats[i].echo_124_count, stats[i].service_123_count,
stats[i].echo_123_count);
LOG_INF("+---------+------+------+------+------+------+------+------+------+------+"
"------+------+------+------+------+------+------+");
}
}
int main(int argc, char **argv)
{
struct net_if *iface = NULL;
uint32_t simulated_work_times[] = {
800, 850, 900, 950, 1000, 1100, 1200, 1300, 1400, 1600, 1800,
2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 15000,
};
struct statistics stats_no_filter[ARRAY_SIZE(simulated_work_times)];
struct statistics stats_with_filter[ARRAY_SIZE(simulated_work_times)];
iface = net_if_lookup_by_dev(DEVICE_GET(net_if_fake));
if (iface == NULL) {
LOG_ERR("No device");
return 1;
}
for (size_t i = 0; i < ARRAY_SIZE(simulated_work_times); ++i) {
stats_no_filter[i] = single_run_with_simulated_work(iface, simulated_work_times[i]);
k_msleep(200);
print_result("In Progress", i + 1, simulated_work_times, stats_no_filter);
/* let simulation settle down */
k_msleep(800);
}
npf_append_recv_rule(&priority_rule_123);
npf_append_recv_rule(&priority_rule_124);
npf_append_recv_rule(&priority_rule_125);
npf_append_recv_rule(&priority_rule_126);
npf_append_recv_rule(&priority_rule_127);
npf_append_recv_rule(&priority_rule_128);
npf_append_recv_rule(&priority_rule_129);
npf_append_recv_rule(&priority_rule_130);
npf_append_recv_rule(&npf_default_ok);
for (size_t i = 0; i < ARRAY_SIZE(simulated_work_times); ++i) {
stats_with_filter[i] =
single_run_with_simulated_work(iface, simulated_work_times[i]);
k_msleep(1000);
print_result("In Progress", i + 1, simulated_work_times, stats_with_filter);
/* let simulation settle down */
k_msleep(2000);
}
k_msleep(4000);
print_result("No Quality of Service Filtering", ARRAY_SIZE(simulated_work_times),
simulated_work_times, stats_no_filter);
print_result("With Quality of Service Filtering", ARRAY_SIZE(simulated_work_times),
simulated_work_times, stats_with_filter);
return 0;
}
static enum net_verdict l2_service(struct net_if *iface, uint16_t ptype, struct net_pkt *pkt)
{
struct net_pkt *echo_reply;
uint8_t type;
const char *service_type;
net_pkt_cursor_init(pkt);
net_pkt_read_u8(pkt, &type);
service_type = (type == SERVICE_TYPE_ECHO) ? "echo" : "command";
LOG_INF("handler for %s-service %d, iface %p, ptype %u, pkt %p", service_type,
net_pkt_ll_proto_type(pkt), iface, ptype, pkt);
posix_cpu_hold(simulated_work_time);
if (type == SERVICE_TYPE_ECHO) {
echo_reply = net_pkt_alloc_with_buffer(iface, 12, AF_UNSPEC, 0, K_NO_WAIT);
if (echo_reply) {
net_pkt_set_ll_proto_type(echo_reply, net_pkt_ll_proto_type(pkt));
net_pkt_set_priority(echo_reply, net_pkt_priority(pkt));
if (net_if_try_send_data(iface, echo_reply, K_NO_WAIT) != NET_OK) {
net_pkt_unref(echo_reply);
}
}
net_pkt_unref(pkt);
return NET_OK;
}
switch (net_pkt_ll_proto_type(pkt)) {
case NET_ETH_PTYPE_123:
k_sem_give(&service_123_received);
break;
case NET_ETH_PTYPE_124:
k_sem_give(&service_124_received);
break;
case NET_ETH_PTYPE_125:
k_sem_give(&service_125_received);
break;
case NET_ETH_PTYPE_126:
k_sem_give(&service_126_received);
break;
case NET_ETH_PTYPE_127:
k_sem_give(&service_127_received);
break;
case NET_ETH_PTYPE_128:
k_sem_give(&service_128_received);
break;
case NET_ETH_PTYPE_129:
k_sem_give(&service_129_received);
break;
case NET_ETH_PTYPE_130:
k_sem_give(&service_130_received);
break;
default: /* nothing to do */
break;
}
net_pkt_unref(pkt);
return NET_OK;
}
ETH_NET_L3_REGISTER(service_123, NET_ETH_PTYPE_123, l2_service);
ETH_NET_L3_REGISTER(service_124, NET_ETH_PTYPE_124, l2_service);
ETH_NET_L3_REGISTER(service_125, NET_ETH_PTYPE_125, l2_service);
ETH_NET_L3_REGISTER(service_126, NET_ETH_PTYPE_126, l2_service);
ETH_NET_L3_REGISTER(service_127, NET_ETH_PTYPE_127, l2_service);
ETH_NET_L3_REGISTER(service_128, NET_ETH_PTYPE_128, l2_service);
ETH_NET_L3_REGISTER(service_129, NET_ETH_PTYPE_129, l2_service);
ETH_NET_L3_REGISTER(service_130, NET_ETH_PTYPE_130, l2_service);