| /* main.c - Application main entry point */ |
| |
| /* |
| * Copyright (c) 2016 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(net_test, CONFIG_NET_ARP_LOG_LEVEL); |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/linker/sections.h> |
| |
| #include <zephyr/tc_util.h> |
| |
| #include <zephyr/types.h> |
| #include <stddef.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <zephyr/device.h> |
| #include <zephyr/init.h> |
| #include <zephyr/net/net_core.h> |
| #include <zephyr/net/net_pkt.h> |
| #include <zephyr/net/net_ip.h> |
| #include <zephyr/net/dummy.h> |
| #include <zephyr/ztest.h> |
| #include <zephyr/random/rand32.h> |
| |
| #include "arp.h" |
| |
| #define NET_LOG_ENABLED 1 |
| #include "net_private.h" |
| |
| static bool req_test; |
| |
| static char *app_data = "0123456789"; |
| |
| static bool entry_found; |
| static struct net_eth_addr *expected_hwaddr; |
| |
| static struct net_pkt *pending_pkt; |
| |
| static struct net_eth_addr hwaddr = { { 0x42, 0x11, 0x69, 0xde, 0xfa, 0xec } }; |
| |
| static int send_status = -EINVAL; |
| |
| struct net_arp_context { |
| uint8_t mac_addr[sizeof(struct net_eth_addr)]; |
| struct net_linkaddr ll_addr; |
| }; |
| |
| int net_arp_dev_init(const struct device *dev) |
| { |
| struct net_arp_context *net_arp_context = dev->data; |
| |
| net_arp_context = net_arp_context; |
| |
| return 0; |
| } |
| |
| static uint8_t *net_arp_get_mac(const struct device *dev) |
| { |
| struct net_arp_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] = sys_rand32_get(); |
| } |
| |
| return context->mac_addr; |
| } |
| |
| static void net_arp_iface_init(struct net_if *iface) |
| { |
| uint8_t *mac = net_arp_get_mac(net_if_get_device(iface)); |
| |
| net_if_set_link_addr(iface, mac, 6, NET_LINK_ETHERNET); |
| } |
| |
| static int tester_send(const struct device *dev, struct net_pkt *pkt) |
| { |
| struct net_eth_hdr *hdr; |
| |
| if (!pkt->buffer) { |
| printk("No data to send!\n"); |
| return -ENODATA; |
| } |
| |
| hdr = (struct net_eth_hdr *)net_pkt_data(pkt); |
| |
| if (ntohs(hdr->type) == NET_ETH_PTYPE_ARP) { |
| /* First frag has eth hdr */ |
| struct net_arp_hdr *arp_hdr = |
| (struct net_arp_hdr *)pkt->frags->frags; |
| |
| if (ntohs(arp_hdr->opcode) == NET_ARP_REPLY) { |
| if (!req_test && pkt != pending_pkt) { |
| printk("Pending data but to be sent is wrong, " |
| "expecting %p but got %p\n", |
| pending_pkt, pkt); |
| return -EINVAL; |
| } |
| |
| if (!req_test && memcmp(&hdr->dst, &hwaddr, |
| sizeof(struct net_eth_addr))) { |
| char out[sizeof("xx:xx:xx:xx:xx:xx")]; |
| |
| snprintk(out, sizeof(out), "%s", |
| net_sprint_ll_addr( |
| (uint8_t *)&hdr->dst, |
| sizeof(struct net_eth_addr))); |
| printk("Invalid dst hwaddr %s, should be %s\n", |
| out, |
| net_sprint_ll_addr( |
| (uint8_t *)&hwaddr, |
| sizeof(struct net_eth_addr))); |
| send_status = -EINVAL; |
| return send_status; |
| } |
| |
| } else if (ntohs(arp_hdr->opcode) == NET_ARP_REQUEST) { |
| if (memcmp(&hdr->src, &hwaddr, |
| sizeof(struct net_eth_addr))) { |
| char out[sizeof("xx:xx:xx:xx:xx:xx")]; |
| |
| snprintk(out, sizeof(out), "%s", |
| net_sprint_ll_addr( |
| (uint8_t *)&hdr->src, |
| sizeof(struct net_eth_addr))); |
| printk("Invalid src hwaddr %s, should be %s\n", |
| out, |
| net_sprint_ll_addr( |
| (uint8_t *)&hwaddr, |
| sizeof(struct net_eth_addr))); |
| send_status = -EINVAL; |
| return send_status; |
| } |
| } |
| } |
| |
| send_status = 0; |
| |
| return 0; |
| } |
| |
| static inline struct in_addr *if_get_addr(struct net_if *iface) |
| { |
| int i; |
| |
| for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { |
| if (iface->config.ip.ipv4->unicast[i].is_used && |
| iface->config.ip.ipv4->unicast[i].address.family == |
| AF_INET && |
| iface->config.ip.ipv4->unicast[i].addr_state == |
| NET_ADDR_PREFERRED) { |
| return |
| &iface->config.ip.ipv4->unicast[i].address.in_addr; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static inline struct net_pkt *prepare_arp_reply(struct net_if *iface, |
| struct net_pkt *req, |
| struct net_eth_addr *addr, |
| struct net_eth_hdr **eth_rep) |
| { |
| struct net_pkt *pkt; |
| struct net_arp_hdr *hdr; |
| struct net_eth_hdr *eth; |
| |
| pkt = net_pkt_alloc_with_buffer(iface, sizeof(struct net_eth_hdr) + |
| sizeof(struct net_eth_hdr) + |
| sizeof(struct net_arp_hdr), |
| AF_UNSPEC, 0, K_SECONDS(1)); |
| zassert_not_null(pkt, "out of mem reply"); |
| |
| eth = NET_ETH_HDR(pkt); |
| |
| net_buf_add(pkt->buffer, sizeof(struct net_eth_hdr)); |
| net_buf_pull(pkt->buffer, sizeof(struct net_eth_hdr)); |
| |
| (void)memset(ð->dst.addr, 0xff, sizeof(struct net_eth_addr)); |
| memcpy(ð->src.addr, net_if_get_link_addr(iface)->addr, |
| sizeof(struct net_eth_addr)); |
| eth->type = htons(NET_ETH_PTYPE_ARP); |
| |
| *eth_rep = eth; |
| |
| net_buf_add(pkt->buffer, sizeof(struct net_eth_hdr)); |
| net_buf_pull(pkt->buffer, sizeof(struct net_eth_hdr)); |
| |
| hdr = NET_ARP_HDR(pkt); |
| |
| hdr->hwtype = htons(NET_ARP_HTYPE_ETH); |
| hdr->protocol = htons(NET_ETH_PTYPE_IP); |
| hdr->hwlen = sizeof(struct net_eth_addr); |
| hdr->protolen = sizeof(struct in_addr); |
| hdr->opcode = htons(NET_ARP_REPLY); |
| |
| memcpy(&hdr->dst_hwaddr.addr, ð->src.addr, |
| sizeof(struct net_eth_addr)); |
| memcpy(&hdr->src_hwaddr.addr, addr, |
| sizeof(struct net_eth_addr)); |
| |
| net_ipv4_addr_copy_raw(hdr->dst_ipaddr, NET_ARP_HDR(req)->src_ipaddr); |
| net_ipv4_addr_copy_raw(hdr->src_ipaddr, NET_ARP_HDR(req)->dst_ipaddr); |
| |
| net_buf_add(pkt->buffer, sizeof(struct net_arp_hdr)); |
| |
| return pkt; |
| } |
| |
| static inline struct net_pkt *prepare_arp_request(struct net_if *iface, |
| struct net_pkt *req, |
| struct net_eth_addr *addr, |
| struct net_eth_hdr **eth_hdr) |
| { |
| struct net_pkt *pkt; |
| struct net_arp_hdr *hdr, *req_hdr; |
| struct net_eth_hdr *eth, *eth_req; |
| |
| pkt = net_pkt_alloc_with_buffer(iface, sizeof(struct net_eth_hdr) + |
| sizeof(struct net_arp_hdr), |
| AF_UNSPEC, 0, K_SECONDS(1)); |
| zassert_not_null(pkt, "out of mem request"); |
| |
| eth_req = NET_ETH_HDR(req); |
| eth = NET_ETH_HDR(pkt); |
| |
| net_buf_add(req->buffer, sizeof(struct net_eth_hdr)); |
| net_buf_pull(req->buffer, sizeof(struct net_eth_hdr)); |
| |
| req_hdr = NET_ARP_HDR(req); |
| |
| (void)memset(ð->dst.addr, 0xff, sizeof(struct net_eth_addr)); |
| memcpy(ð->src.addr, addr, sizeof(struct net_eth_addr)); |
| |
| eth->type = htons(NET_ETH_PTYPE_ARP); |
| *eth_hdr = eth; |
| |
| net_buf_add(pkt->buffer, sizeof(struct net_eth_hdr)); |
| net_buf_pull(pkt->buffer, sizeof(struct net_eth_hdr)); |
| |
| hdr = NET_ARP_HDR(pkt); |
| |
| hdr->hwtype = htons(NET_ARP_HTYPE_ETH); |
| hdr->protocol = htons(NET_ETH_PTYPE_IP); |
| hdr->hwlen = sizeof(struct net_eth_addr); |
| hdr->protolen = sizeof(struct in_addr); |
| hdr->opcode = htons(NET_ARP_REQUEST); |
| |
| (void)memset(&hdr->dst_hwaddr.addr, 0x00, sizeof(struct net_eth_addr)); |
| memcpy(&hdr->src_hwaddr.addr, addr, sizeof(struct net_eth_addr)); |
| |
| net_ipv4_addr_copy_raw(hdr->src_ipaddr, req_hdr->src_ipaddr); |
| net_ipv4_addr_copy_raw(hdr->dst_ipaddr, req_hdr->dst_ipaddr); |
| |
| net_buf_add(pkt->buffer, sizeof(struct net_arp_hdr)); |
| |
| return pkt; |
| } |
| |
| static void setup_eth_header(struct net_if *iface, struct net_pkt *pkt, |
| const struct net_eth_addr *hwaddr, uint16_t type) |
| { |
| struct net_eth_hdr *hdr = (struct net_eth_hdr *)net_pkt_data(pkt); |
| |
| memcpy(&hdr->dst.addr, hwaddr, sizeof(struct net_eth_addr)); |
| memcpy(&hdr->src.addr, net_if_get_link_addr(iface)->addr, |
| sizeof(struct net_eth_addr)); |
| |
| hdr->type = htons(type); |
| } |
| |
| struct net_arp_context net_arp_context_data; |
| |
| #if defined(CONFIG_NET_ARP) && defined(CONFIG_NET_L2_ETHERNET) |
| static const struct ethernet_api net_arp_if_api = { |
| .iface_api.init = net_arp_iface_init, |
| .send = tester_send, |
| }; |
| |
| #define _ETH_L2_LAYER ETHERNET_L2 |
| #define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(ETHERNET_L2) |
| #else |
| static const struct dummy_api net_arp_if_api = { |
| .iface_api.init = net_arp_iface_init, |
| .send = tester_send, |
| }; |
| |
| #define _ETH_L2_LAYER DUMMY_L2 |
| #define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(DUMMY_L2) |
| #endif |
| |
| NET_DEVICE_INIT(net_arp_test, "net_arp_test", |
| net_arp_dev_init, NULL, |
| &net_arp_context_data, NULL, |
| CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, |
| &net_arp_if_api, _ETH_L2_LAYER, _ETH_L2_CTX_TYPE, 127); |
| |
| static void arp_cb(struct arp_entry *entry, void *user_data) |
| { |
| struct in_addr *addr = user_data; |
| |
| if (memcmp(&entry->ip, addr, sizeof(struct in_addr)) == 0 && |
| memcmp(&entry->eth, expected_hwaddr, |
| sizeof(struct net_eth_addr)) == 0) { |
| entry_found = true; |
| } |
| } |
| |
| ZTEST(arp_fn_tests, test_arp) |
| { |
| if (IS_ENABLED(CONFIG_NET_TC_THREAD_COOPERATIVE)) { |
| k_thread_priority_set(k_current_get(), |
| K_PRIO_COOP(CONFIG_NUM_COOP_PRIORITIES - 1)); |
| } else { |
| k_thread_priority_set(k_current_get(), K_PRIO_PREEMPT(9)); |
| } |
| |
| struct net_eth_hdr *eth_hdr = NULL; |
| struct net_pkt *pkt; |
| struct net_pkt *pkt2; |
| struct net_if *iface; |
| struct net_if_addr *ifaddr; |
| struct net_arp_hdr *arp_hdr; |
| struct net_ipv4_hdr *ipv4; |
| int len; |
| |
| struct in_addr dst = { { { 192, 168, 0, 2 } } }; |
| struct in_addr dst_far = { { { 10, 11, 12, 13 } } }; |
| struct in_addr dst_far2 = { { { 172, 16, 14, 186 } } }; |
| struct in_addr src = { { { 192, 168, 0, 1 } } }; |
| struct in_addr netmask = { { { 255, 255, 255, 0 } } }; |
| struct in_addr gw = { { { 192, 168, 0, 42 } } }; |
| |
| net_arp_init(); |
| |
| iface = net_if_lookup_by_dev(DEVICE_GET(net_arp_test)); |
| |
| net_if_ipv4_set_gw(iface, &gw); |
| net_if_ipv4_set_netmask(iface, &netmask); |
| |
| /* Unicast test */ |
| ifaddr = net_if_ipv4_addr_add(iface, |
| &src, |
| NET_ADDR_MANUAL, |
| 0); |
| zassert_not_null(ifaddr, "Cannot add address"); |
| ifaddr->addr_state = NET_ADDR_PREFERRED; |
| |
| len = strlen(app_data); |
| |
| /* Application data for testing */ |
| pkt = net_pkt_alloc_with_buffer(iface, sizeof(struct net_ipv4_hdr) + |
| len, AF_INET, 0, K_SECONDS(1)); |
| zassert_not_null(pkt, "out of mem"); |
| |
| net_pkt_lladdr_src(pkt)->addr = (uint8_t *)net_if_get_link_addr(iface); |
| net_pkt_lladdr_src(pkt)->len = sizeof(struct net_eth_addr); |
| |
| ipv4 = (struct net_ipv4_hdr *)net_buf_add(pkt->buffer, |
| sizeof(struct net_ipv4_hdr)); |
| net_ipv4_addr_copy_raw(ipv4->src, (uint8_t *)&src); |
| net_ipv4_addr_copy_raw(ipv4->dst, (uint8_t *)&dst); |
| |
| memcpy(net_buf_add(pkt->buffer, len), app_data, len); |
| |
| pkt2 = net_arp_prepare(pkt, &dst, NULL); |
| |
| /* pkt2 is the ARP packet and pkt is the IPv4 packet and it was |
| * stored in ARP table. |
| */ |
| |
| /**TESTPOINTS: Check packets*/ |
| zassert_not_equal((void *)(pkt2), (void *)(pkt), |
| /* The packets cannot be the same as the ARP cache has |
| * still room for the pkt. |
| */ |
| "ARP cache should still have free space"); |
| |
| zassert_not_null(pkt2, "ARP pkt is empty"); |
| |
| /* The ARP cache should now have a link to pending net_pkt |
| * that is to be sent after we have got an ARP reply. |
| */ |
| zassert_not_null(pkt->buffer, |
| "Pending pkt buffer is NULL"); |
| |
| pending_pkt = pkt; |
| |
| /* pkt2 should contain the arp header, verify it */ |
| arp_hdr = NET_ARP_HDR(pkt2); |
| |
| if (arp_hdr->hwtype != htons(NET_ARP_HTYPE_ETH)) { |
| printk("ARP hwtype 0x%x, should be 0x%x\n", |
| arp_hdr->hwtype, htons(NET_ARP_HTYPE_ETH)); |
| zassert_true(0, "exiting"); |
| } |
| |
| if (arp_hdr->protocol != htons(NET_ETH_PTYPE_IP)) { |
| printk("ARP protocol 0x%x, should be 0x%x\n", |
| arp_hdr->protocol, htons(NET_ETH_PTYPE_IP)); |
| zassert_true(0, "exiting"); |
| } |
| |
| if (arp_hdr->hwlen != sizeof(struct net_eth_addr)) { |
| printk("ARP hwlen 0x%x, should be 0x%zx\n", |
| arp_hdr->hwlen, sizeof(struct net_eth_addr)); |
| zassert_true(0, "exiting"); |
| } |
| |
| if (arp_hdr->protolen != sizeof(struct in_addr)) { |
| printk("ARP IP addr len 0x%x, should be 0x%zx\n", |
| arp_hdr->protolen, sizeof(struct in_addr)); |
| zassert_true(0, "exiting"); |
| } |
| |
| if (arp_hdr->opcode != htons(NET_ARP_REQUEST)) { |
| printk("ARP opcode 0x%x, should be 0x%x\n", |
| arp_hdr->opcode, htons(NET_ARP_REQUEST)); |
| zassert_true(0, "exiting"); |
| } |
| |
| if (!net_ipv4_addr_cmp_raw(arp_hdr->dst_ipaddr, |
| NET_IPV4_HDR(pkt)->dst)) { |
| printk("ARP IP dest invalid %s, should be %s", |
| net_sprint_ipv4_addr(&arp_hdr->dst_ipaddr), |
| net_sprint_ipv4_addr(&NET_IPV4_HDR(pkt)->dst)); |
| zassert_true(0, "exiting"); |
| } |
| |
| if (!net_ipv4_addr_cmp_raw(arp_hdr->src_ipaddr, |
| NET_IPV4_HDR(pkt)->src)) { |
| printk("ARP IP src invalid %s, should be %s", |
| net_sprint_ipv4_addr(&arp_hdr->src_ipaddr), |
| net_sprint_ipv4_addr(&NET_IPV4_HDR(pkt)->src)); |
| zassert_true(0, "exiting"); |
| } |
| |
| /* We could have send the new ARP request but for this test we |
| * just free it. |
| */ |
| net_pkt_unref(pkt2); |
| |
| zassert_equal(atomic_get(&pkt->atomic_ref), 2, |
| "ARP cache should own the original packet"); |
| |
| /* Then a case where target is not in the same subnet */ |
| net_ipv4_addr_copy_raw(ipv4->dst, (uint8_t *)&dst_far); |
| |
| pkt2 = net_arp_prepare(pkt, &dst_far, NULL); |
| |
| zassert_not_equal((void *)(pkt2), (void *)(pkt), |
| "ARP cache should not find anything"); |
| |
| /**TESTPOINTS: Check if packets not empty*/ |
| zassert_not_null(pkt2, |
| "ARP pkt2 is empty"); |
| |
| arp_hdr = NET_ARP_HDR(pkt2); |
| |
| if (!net_ipv4_addr_cmp_raw(arp_hdr->dst_ipaddr, |
| (uint8_t *)&iface->config.ip.ipv4->gw)) { |
| printk("ARP IP dst invalid %s, should be %s\n", |
| net_sprint_ipv4_addr(&arp_hdr->dst_ipaddr), |
| net_sprint_ipv4_addr(&iface->config.ip.ipv4->gw)); |
| zassert_true(0, "exiting"); |
| } |
| |
| net_pkt_unref(pkt2); |
| |
| /* Try to find the same destination again, this should fail as there |
| * is a pending request in ARP cache. |
| */ |
| net_ipv4_addr_copy_raw(ipv4->dst, (uint8_t *)&dst_far); |
| |
| /* Make sure prepare will not free the pkt because it will be |
| * needed in the later test case. |
| */ |
| net_pkt_ref(pkt); |
| |
| pkt2 = net_arp_prepare(pkt, &dst_far, NULL); |
| |
| zassert_not_null(pkt2, |
| "ARP cache is not sending the request again"); |
| |
| net_pkt_unref(pkt2); |
| |
| /* Try to find the different destination, this should fail too |
| * as the cache table should be full. |
| */ |
| net_ipv4_addr_copy_raw(ipv4->dst, (uint8_t *)&dst_far2); |
| |
| /* Make sure prepare will not free the pkt because it will be |
| * needed in the next test case. |
| */ |
| net_pkt_ref(pkt); |
| |
| pkt2 = net_arp_prepare(pkt, &dst_far2, NULL); |
| |
| zassert_not_null(pkt2, |
| "ARP cache did not send a req"); |
| |
| /* Restore the original address so that following test case can |
| * work properly. |
| */ |
| net_ipv4_addr_copy_raw(ipv4->dst, (uint8_t *)&dst); |
| |
| /* The arp request packet is now verified, create an arp reply. |
| * The previous value of pkt is stored in arp table and is not lost. |
| */ |
| pkt = net_pkt_alloc_with_buffer(iface, sizeof(struct net_eth_hdr) + |
| sizeof(struct net_arp_hdr), |
| AF_UNSPEC, 0, K_SECONDS(1)); |
| zassert_not_null(pkt, "out of mem reply"); |
| |
| arp_hdr = NET_ARP_HDR(pkt); |
| net_buf_add(pkt->buffer, sizeof(struct net_arp_hdr)); |
| |
| net_ipv4_addr_copy_raw(arp_hdr->dst_ipaddr, (uint8_t *)&dst); |
| net_ipv4_addr_copy_raw(arp_hdr->src_ipaddr, (uint8_t *)&src); |
| |
| pkt2 = prepare_arp_reply(iface, pkt, &hwaddr, ð_hdr); |
| |
| zassert_not_null(pkt2, "ARP reply generation failed."); |
| |
| /* The pending packet should now be sent */ |
| switch (net_arp_input(pkt2, eth_hdr)) { |
| case NET_OK: |
| case NET_CONTINUE: |
| break; |
| case NET_DROP: |
| break; |
| } |
| |
| /* Yielding so that network interface TX thread can proceed. */ |
| k_yield(); |
| |
| /**TESTPOINTS: Check ARP reply*/ |
| zassert_false(send_status < 0, "ARP reply was not sent"); |
| |
| zassert_equal(atomic_get(&pkt->atomic_ref), 1, |
| "ARP cache should no longer own the original packet"); |
| |
| net_pkt_unref(pkt); |
| |
| /* Then feed in ARP request */ |
| pkt = net_pkt_alloc_with_buffer(iface, sizeof(struct net_eth_hdr) + |
| sizeof(struct net_arp_hdr), |
| AF_UNSPEC, 0, K_SECONDS(1)); |
| zassert_not_null(pkt, "out of mem reply"); |
| |
| send_status = -EINVAL; |
| |
| setup_eth_header(iface, pkt, &hwaddr, NET_ETH_PTYPE_ARP); |
| |
| arp_hdr = (struct net_arp_hdr *)(pkt->buffer->data + |
| (sizeof(struct net_eth_hdr))); |
| net_buf_add(pkt->buffer, sizeof(struct net_arp_hdr)); |
| |
| net_ipv4_addr_copy_raw(arp_hdr->dst_ipaddr, (uint8_t *)&src); |
| net_ipv4_addr_copy_raw(arp_hdr->src_ipaddr, (uint8_t *)&dst); |
| |
| pkt2 = prepare_arp_request(iface, pkt, &hwaddr, ð_hdr); |
| |
| /**TESTPOINT: Check if ARP request generation failed*/ |
| zassert_not_null(pkt2, "ARP request generation failed."); |
| |
| req_test = true; |
| |
| switch (net_arp_input(pkt2, eth_hdr)) { |
| case NET_OK: |
| case NET_CONTINUE: |
| break; |
| case NET_DROP: |
| break; |
| } |
| |
| /* Yielding so that network interface TX thread can proceed. */ |
| k_yield(); |
| |
| /**TESTPOINT: Check if ARP request sent*/ |
| zassert_false(send_status < 0, "ARP req was not sent"); |
| |
| net_pkt_unref(pkt); |
| |
| /**TESTPOINT: Check gratuitous ARP */ |
| if (IS_ENABLED(CONFIG_NET_ARP_GRATUITOUS)) { |
| struct net_eth_addr new_hwaddr = { |
| { 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 } |
| }; |
| enum net_verdict verdict; |
| |
| /* First make sure that we have an entry in cache */ |
| entry_found = false; |
| expected_hwaddr = &hwaddr; |
| net_arp_foreach(arp_cb, &dst); |
| zassert_true(entry_found, "Entry not found"); |
| |
| pkt = net_pkt_alloc_with_buffer(iface, |
| sizeof(struct net_eth_hdr) + |
| sizeof(struct net_arp_hdr), |
| AF_UNSPEC, 0, K_SECONDS(1)); |
| zassert_not_null(pkt, "out of mem request"); |
| |
| setup_eth_header(iface, pkt, net_eth_broadcast_addr(), |
| NET_ETH_PTYPE_ARP); |
| |
| eth_hdr = (struct net_eth_hdr *)net_pkt_data(pkt); |
| net_buf_add(pkt->buffer, sizeof(struct net_eth_hdr)); |
| net_buf_pull(pkt->buffer, sizeof(struct net_eth_hdr)); |
| arp_hdr = NET_ARP_HDR(pkt); |
| |
| arp_hdr->hwtype = htons(NET_ARP_HTYPE_ETH); |
| arp_hdr->protocol = htons(NET_ETH_PTYPE_IP); |
| arp_hdr->hwlen = sizeof(struct net_eth_addr); |
| arp_hdr->protolen = sizeof(struct in_addr); |
| arp_hdr->opcode = htons(NET_ARP_REQUEST); |
| memcpy(&arp_hdr->src_hwaddr, &new_hwaddr, 6); |
| memcpy(&arp_hdr->dst_hwaddr, net_eth_broadcast_addr(), 6); |
| net_ipv4_addr_copy_raw(arp_hdr->dst_ipaddr, (uint8_t *)&dst); |
| net_ipv4_addr_copy_raw(arp_hdr->src_ipaddr, (uint8_t *)&dst); |
| |
| net_buf_add(pkt->buffer, sizeof(struct net_arp_hdr)); |
| |
| verdict = net_arp_input(pkt, eth_hdr); |
| zassert_not_equal(verdict, NET_DROP, "Gratuitous ARP failed"); |
| |
| /* Then check that the HW address is changed for an existing |
| * entry. |
| */ |
| entry_found = false; |
| expected_hwaddr = &new_hwaddr; |
| net_arp_foreach(arp_cb, &dst); |
| zassert_true(entry_found, "Changed entry not found"); |
| |
| net_pkt_unref(pkt); |
| } |
| } |
| |
| ZTEST_SUITE(arp_fn_tests, NULL, NULL, NULL, NULL, NULL); |