blob: afa5c0072802157e6a37d46c56d4cd2894b6d0fc [file] [log] [blame]
/* main.c - Application main entry point */
/*
* Copyright (c) 2019 Intel Corporation
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(net_test, CONFIG_NET_ICMPV4_LOG_LEVEL);
#include <errno.h>
#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <zephyr/sys/printk.h>
#include <zephyr/linker/sections.h>
#include <zephyr/tc_util.h>
#include <zephyr/net/buf.h>
#include <zephyr/net/ethernet.h>
#include <zephyr/net/dummy.h>
#include <zephyr/net/icmp.h>
#include "net_private.h"
#include "icmpv4.h"
#include "ipv4.h"
#include <zephyr/ztest.h>
static const unsigned char icmpv4_echo_req[] = {
/* IPv4 Header */
0x45, 0x00, 0x00, 0x54, 0xea, 0x8c, 0x40, 0x00,
0x40, 0x01, 0xcc, 0x18, 0xc0, 0x00, 0x02, 0x02,
0xc0, 0x00, 0x02, 0x01,
/* ICMP Header (Echo Request) */
0x08, 0x00, 0xe3, 0x7c,
0x10, 0x63, 0x00, 0x01,
/* Payload */
0xb8, 0xa4, 0x8c, 0x5d, 0x00, 0x00, 0x00, 0x00,
0xfc, 0x49, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 };
static const unsigned char icmpv4_echo_rep[] = {
/* IPv4 Header */
0x45, 0x00, 0x00, 0x20, 0x75, 0xac, 0x00, 0x00,
0x40, 0x01, 0x81, 0x2d, 0xc0, 0x00, 0x02, 0x02,
0xc0, 0x00, 0x02, 0x01,
/* ICMP Header (Echo Reply)*/
0x00, 0x00, 0x91, 0x12,
0x16, 0x50, 0x00, 0x00, 0x01, 0xfd, 0x56, 0xa0 };
static const unsigned char icmpv4_echo_req_opt[] = {
/* IPv4 Header */
0x4e, 0x00, 0x00, 0x78, 0xe1, 0x4b, 0x40, 0x00,
0x40, 0x01, 0x9a, 0x83, 0xc0, 0x00, 0x02, 0x02,
0xc0, 0x00, 0x02, 0x01,
/* IPv4 Header Options (Timestamp) */
0x44, 0x24, 0x0d, 0x01, 0xc0, 0x00, 0x02, 0x02,
0x02, 0x4d, 0x1c, 0x3d, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
/* ICMP Header (Echo Request) */
0x08, 0x00, 0x35, 0xbf,
0x5d, 0xe7, 0x00, 0x01,
0xcf, 0xe7, 0x8d, 0x5d, 0x00, 0x00, 0x00, 0x00,
/* Payload */
0x3a, 0x40, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 };
static const unsigned char icmpv4_echo_req_opt_bad[] = {
/* IPv4 Header */
0x46, 0x00, 0x00, 0xa0, 0xf8, 0x6c, 0x00, 0x00,
0x64, 0x01, 0x56, 0xa8, 0xc0, 0x00, 0x02, 0x02,
0xc0, 0x00, 0x02, 0x01,
/* IPv4 Header Options (Wrong length) */
0x41, 0x03, 0x41, 0x41,
/* ICMP Header (Echo Request) */
0x08, 0x00, 0x06, 0xb8, 0x30, 0x31, 0x32, 0x07,
/* Payload */
0x80, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00 };
#define TEST_ICMPV4_UNKNOWN 0
#define TEST_ICMPV4_ECHO_REQ 1
#define TEST_ICMPV4_ECHO_REQ_OPTS 2
static uint8_t current = TEST_ICMPV4_UNKNOWN;
static struct in_addr my_addr = { { { 192, 0, 2, 1 } } };
static struct net_if *net_iface;
static int handle_reply_msg(struct net_icmp_ctx *ctx,
struct net_pkt *pkt,
struct net_icmp_ip_hdr *hdr,
struct net_icmp_hdr *icmp_hdr,
void *user_data)
{
ARG_UNUSED(ctx);
ARG_UNUSED(hdr);
ARG_UNUSED(icmp_hdr);
ARG_UNUSED(user_data);
if (net_pkt_get_len(pkt) != sizeof(icmpv4_echo_rep)) {
return -ENOMSG;
}
return 0;
}
struct net_icmpv4_context {
uint8_t mac_addr[sizeof(struct net_eth_addr)];
struct net_linkaddr ll_addr;
};
static int net_icmpv4_dev_init(const struct device *dev)
{
struct net_icmpv4_context *net_icmpv4_context = dev->data;
net_icmpv4_context = net_icmpv4_context;
return 0;
}
static uint8_t *net_icmpv4_get_mac(const struct device *dev)
{
struct net_icmpv4_context *context = dev->data;
if (context->mac_addr[2] == 0x00) {
/* 00-00-5E-00-53-xx Documentation RFC 7042 */
context->mac_addr[0] = 0x00;
context->mac_addr[1] = 0x00;
context->mac_addr[2] = 0x5E;
context->mac_addr[3] = 0x00;
context->mac_addr[4] = 0x53;
context->mac_addr[5] = 0x01;
}
return context->mac_addr;
}
static void net_icmpv4_iface_init(struct net_if *iface)
{
uint8_t *mac = net_icmpv4_get_mac(net_if_get_device(iface));
net_if_set_link_addr(iface, mac, 6, NET_LINK_ETHERNET);
}
static int verify_echo_reply(struct net_pkt *pkt)
{
struct net_icmp_hdr icmp_hdr;
uint8_t buf[60];
int ret;
uint8_t payload_len;
net_pkt_set_overwrite(pkt, true);
net_pkt_cursor_init(pkt);
ret = net_pkt_skip(pkt, NET_IPV4H_LEN);
if (ret != 0) {
zassert_true(false, "echo_reply skip failed");
}
/* EchoReply Code and Type is 0 */
ret = net_pkt_read(pkt, &icmp_hdr, sizeof(struct net_icmp_hdr));
if (ret != 0) {
zassert_true(false, "echo_reply read failed");
}
if (icmp_hdr.code != 0 || icmp_hdr.type != 0) {
zassert_true(false, "echo_reply invalid type or code");
}
/* Calculate payload length */
payload_len = sizeof(icmpv4_echo_req) -
NET_IPV4H_LEN - NET_ICMPH_LEN;
if (payload_len != net_pkt_remaining_data(pkt)) {
zassert_true(false, "echo_reply invalid payload len");
}
ret = net_pkt_read(pkt, buf, payload_len);
if (ret != 0) {
zassert_true(false, "echo_reply read payload failed");
}
/* Compare the payload */
if (memcmp(buf, icmpv4_echo_req + NET_IPV4H_LEN + NET_ICMPH_LEN,
payload_len)) {
zassert_true(false, "echo_reply invalid payload");
}
/* Options length should be zero */
if (net_pkt_ipv4_opts_len(pkt)) {
zassert_true(false, "echo_reply invalid opts len");
}
return 0;
}
static int verify_echo_reply_with_opts(struct net_pkt *pkt)
{
struct net_icmp_hdr icmp_hdr;
uint8_t buf[60];
int ret;
uint8_t vhl;
uint8_t opts_len;
uint8_t payload_len;
net_pkt_set_overwrite(pkt, true);
net_pkt_cursor_init(pkt);
ret = net_pkt_read_u8(pkt, &vhl);
if (ret != 0) {
zassert_true(false, "echo_reply_opts read failed");
}
vhl = (vhl & NET_IPV4_IHL_MASK) * 4U;
opts_len = vhl - sizeof(struct net_ipv4_hdr);
if (opts_len == 0) {
zassert_true(false, "echo_reply_opts wrong opts len");
}
ret = net_pkt_skip(pkt, NET_IPV4H_LEN - 1U + opts_len);
if (ret != 0) {
zassert_true(false, "echo_reply_opts skip failed");
}
/* EchoReply Code and Type is 0 */
ret = net_pkt_read(pkt, &icmp_hdr, sizeof(struct net_icmp_hdr));
if (ret != 0) {
zassert_true(false, "echo_reply_opts read failed");
}
if (icmp_hdr.code != 0 || icmp_hdr.type != 0) {
zassert_true(false, "echo_reply_opts wrong code and type");
}
/* Calculate payload length */
payload_len = sizeof(icmpv4_echo_req_opt) -
NET_IPV4H_LEN - NET_ICMPH_LEN - opts_len;
if (payload_len != net_pkt_remaining_data(pkt)) {
zassert_true(false, "echo_reply_opts invalid payload len");
}
ret = net_pkt_read(pkt, buf, payload_len);
if (ret != 0) {
zassert_true(false, "echo_reply_opts read payload failed");
}
/* Compare the payload */
if (memcmp(buf, icmpv4_echo_req_opt +
NET_IPV4H_LEN + NET_ICMPH_LEN + opts_len,
payload_len)) {
zassert_true(false, "echo_reply_opts invalid payload");
}
/* Options length should not be zero */
if (net_pkt_ipv4_opts_len(pkt) != opts_len) {
zassert_true(false, "echo_reply_opts wrong opts len");
}
return 0;
}
static int tester_send(const struct device *dev, struct net_pkt *pkt)
{
if (current == TEST_ICMPV4_ECHO_REQ) {
return verify_echo_reply(pkt);
} else if (current == TEST_ICMPV4_ECHO_REQ_OPTS) {
return verify_echo_reply_with_opts(pkt);
}
return -EINVAL;
}
struct net_icmpv4_context net_icmpv4_context_data;
static struct dummy_api net_icmpv4_if_api = {
.iface_api.init = net_icmpv4_iface_init,
.send = tester_send,
};
NET_DEVICE_INIT(net_icmpv4_test, "net_icmpv4_test",
net_icmpv4_dev_init, NULL,
&net_icmpv4_context_data, NULL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
&net_icmpv4_if_api, DUMMY_L2,
NET_L2_GET_CTX_TYPE(DUMMY_L2), 127);
static struct net_pkt *prepare_echo_request(struct net_if *iface)
{
struct net_pkt *pkt;
pkt = net_pkt_alloc_with_buffer(iface, sizeof(icmpv4_echo_req),
AF_INET, IPPROTO_ICMP, K_FOREVER);
if (!pkt) {
return NULL;
}
if (net_pkt_write(pkt, icmpv4_echo_req, sizeof(icmpv4_echo_req))) {
goto fail;
}
net_pkt_set_overwrite(pkt, true);
net_pkt_cursor_init(pkt);
return pkt;
fail:
net_pkt_unref(pkt);
return NULL;
}
static struct net_pkt *prepare_echo_reply(struct net_if *iface)
{
struct net_pkt *pkt;
pkt = net_pkt_alloc_with_buffer(iface, sizeof(icmpv4_echo_rep),
AF_INET, IPPROTO_ICMP, K_FOREVER);
if (!pkt) {
return NULL;
}
if (net_pkt_write(pkt, icmpv4_echo_rep, sizeof(icmpv4_echo_rep))) {
goto fail;
}
net_pkt_set_overwrite(pkt, true);
net_pkt_cursor_init(pkt);
return pkt;
fail:
net_pkt_unref(pkt);
return NULL;
}
static struct net_pkt *prepare_echo_request_with_options(struct net_if *iface)
{
struct net_pkt *pkt;
pkt = net_pkt_alloc_with_buffer(iface, sizeof(icmpv4_echo_req_opt),
AF_INET, IPPROTO_ICMP, K_FOREVER);
if (!pkt) {
return NULL;
}
if (net_pkt_write(pkt, icmpv4_echo_req_opt,
sizeof(icmpv4_echo_req_opt))) {
goto fail;
}
net_pkt_set_overwrite(pkt, true);
net_pkt_cursor_init(pkt);
return pkt;
fail:
net_pkt_unref(pkt);
return NULL;
}
static struct net_pkt *prepare_echo_request_with_bad_options(
struct net_if *iface)
{
struct net_pkt *pkt;
pkt = net_pkt_alloc_with_buffer(iface, sizeof(icmpv4_echo_req_opt_bad),
AF_INET, IPPROTO_ICMP, K_FOREVER);
if (!pkt) {
return NULL;
}
if (net_pkt_write(pkt, icmpv4_echo_req_opt_bad,
sizeof(icmpv4_echo_req_opt_bad))) {
goto fail;
}
net_pkt_set_overwrite(pkt, true);
net_pkt_cursor_init(pkt);
return pkt;
fail:
net_pkt_unref(pkt);
return NULL;
}
static void *icmpv4_setup(void)
{
struct net_if_addr *ifaddr;
net_iface = net_if_get_first_by_type(&NET_L2_GET_NAME(DUMMY));
if (!net_iface) {
zassert_true(false, "Interface not available");
}
ifaddr = net_if_ipv4_addr_add(net_iface, &my_addr, NET_ADDR_MANUAL, 0);
if (!ifaddr) {
zassert_true(false, "Failed to add address");
}
return NULL;
}
static void icmpv4_teardown(void *dummy)
{
ARG_UNUSED(dummy);
net_iface = net_if_get_first_by_type(&NET_L2_GET_NAME(DUMMY));
net_if_ipv4_addr_rm(net_iface, &my_addr);
}
static void icmpv4_send_echo_req(void)
{
struct net_pkt *pkt;
current = TEST_ICMPV4_ECHO_REQ;
pkt = prepare_echo_request(net_iface);
if (!pkt) {
zassert_true(false, "EchoRequest packet prep failed");
}
if (net_ipv4_input(pkt, false)) {
net_pkt_unref(pkt);
zassert_true(false, "Failed to send");
}
}
static void icmpv4_send_echo_rep(void)
{
static struct net_icmp_ctx ctx;
struct net_pkt *pkt;
int ret;
ret = net_icmp_init_ctx(&ctx, NET_ICMPV4_ECHO_REPLY,
0, handle_reply_msg);
zassert_equal(ret, 0, "Cannot register %s handler (%d)",
STRINGIFY(NET_ICMPV4_ECHO_REPLY), ret);
pkt = prepare_echo_reply(net_iface);
if (!pkt) {
zassert_true(false, "EchoReply packet prep failed");
}
if (net_ipv4_input(pkt, false)) {
net_pkt_unref(pkt);
zassert_true(false, "Failed to send");
}
ret = net_icmp_cleanup_ctx(&ctx);
zassert_equal(ret, 0, "Cannot unregister handler (%d)", ret);
}
ZTEST(net_icmpv4, test_icmpv4_send_echo_req_opt)
{
struct net_pkt *pkt;
current = TEST_ICMPV4_ECHO_REQ_OPTS;
pkt = prepare_echo_request_with_options(net_iface);
if (!pkt) {
zassert_true(false, "EchoRequest with opts packet prep failed");
}
if (net_ipv4_input(pkt, false)) {
net_pkt_unref(pkt);
zassert_true(false, "Failed to send");
}
}
ZTEST(net_icmpv4, test_send_echo_req_bad_opt)
{
struct net_pkt *pkt;
pkt = prepare_echo_request_with_bad_options(net_iface);
if (!pkt) {
zassert_true(false,
"EchoRequest with bad opts packet prep failed");
}
if (net_ipv4_input(pkt, false)) {
net_pkt_unref(pkt);
}
}
ZTEST(net_icmpv4, test_icmpv4_send_echo)
{
icmpv4_send_echo_req();
icmpv4_send_echo_rep();
}
ZTEST_SUITE(net_icmpv4, NULL, icmpv4_setup, NULL, NULL, icmpv4_teardown);