/* main.c - Application main entry point */ /* * Copyright (c) 2016 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include LOG_MODULE_REGISTER(net_test, CONFIG_NET_ARP_LOG_LEVEL); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 eth_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_rand8_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].ipv4.is_used && iface->config.ip.ipv4->unicast[i].ipv4.address.family == AF_INET && iface->config.ip.ipv4->unicast[i].ipv4.addr_state == NET_ADDR_PREFERRED) { return &iface->config.ip.ipv4->unicast[i].ipv4.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); /* 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; net_if_ipv4_set_netmask_by_addr(iface, &src, &netmask); 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);