test: eth: Add a test for ARP requests
authorJoe Hershberger <joe.hershberger@ni.com>
Wed, 26 Sep 2018 21:49:00 +0000 (16:49 -0500)
committerJoe Hershberger <joe.hershberger@ni.com>
Wed, 10 Oct 2018 17:29:00 +0000 (12:29 -0500)
This tests that ARP requests made to this target's IP address are
responded-to by the target when it is doing other networking operations.

This currently corrupts the ongoing operation of the device if it
happens to be awaiting an ARP reply of its own to whatever serverip it
is attempting to communicate with. In the test, add an expectation that
the user operation (ping, in this case) will fail. A later patch will
address this problem.

Signed-off-by: Joe Hershberger <joe.hershberger@ni.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
arch/sandbox/include/asm/eth.h
drivers/net/sandbox.c
test/dm/eth.c

index a6adec4e83dad1a58e349daa26b90dc5b3262b30..d068486f0bef509c89dbff1b9629934f32aefddd 100644 (file)
@@ -39,6 +39,16 @@ int sandbox_eth_arp_req_to_reply(struct udevice *dev, void *packet,
 int sandbox_eth_ping_req_to_reply(struct udevice *dev, void *packet,
                                  unsigned int len);
 
+/*
+ * sandbox_eth_recv_arp_req()
+ *
+ * Inject an ARP request for this target
+ *
+ * @dev: device that received the packet
+ * @return 0 if injected, -EOVERFLOW if not
+ */
+int sandbox_eth_recv_arp_req(struct udevice *dev);
+
 /**
  * A packet handler
  *
index e26e72ecc1065795b9fae6c4dfca7b57c8729874..81f0764fa6892a1a5ae9617b9c8f5226132754bf 100644 (file)
@@ -160,6 +160,47 @@ int sandbox_eth_ping_req_to_reply(struct udevice *dev, void *packet,
        return 0;
 }
 
+/*
+ * sandbox_eth_recv_arp_req()
+ *
+ * Inject an ARP request for this target
+ *
+ * returns 0 if injected, -EOVERFLOW if not
+ */
+int sandbox_eth_recv_arp_req(struct udevice *dev)
+{
+       struct eth_sandbox_priv *priv = dev_get_priv(dev);
+       struct ethernet_hdr *eth_recv;
+       struct arp_hdr *arp_recv;
+
+       /* Don't allow the buffer to overrun */
+       if (priv->recv_packets >= PKTBUFSRX)
+               return -EOVERFLOW;
+
+       /* Formulate a fake request */
+       eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets];
+       memcpy(eth_recv->et_dest, net_bcast_ethaddr, ARP_HLEN);
+       memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN);
+       eth_recv->et_protlen = htons(PROT_ARP);
+
+       arp_recv = (void *)eth_recv + ETHER_HDR_SIZE;
+       arp_recv->ar_hrd = htons(ARP_ETHER);
+       arp_recv->ar_pro = htons(PROT_IP);
+       arp_recv->ar_hln = ARP_HLEN;
+       arp_recv->ar_pln = ARP_PLEN;
+       arp_recv->ar_op = htons(ARPOP_REQUEST);
+       memcpy(&arp_recv->ar_sha, priv->fake_host_hwaddr, ARP_HLEN);
+       net_write_ip(&arp_recv->ar_spa, priv->fake_host_ipaddr);
+       memcpy(&arp_recv->ar_tha, net_null_ethaddr, ARP_HLEN);
+       net_write_ip(&arp_recv->ar_tpa, net_ip);
+
+       priv->recv_packet_length[priv->recv_packets] =
+               ETHER_HDR_SIZE + ARP_HDR_SIZE;
+       ++priv->recv_packets;
+
+       return 0;
+}
+
 /*
  * sb_default_handler()
  *
index 1a7684a887b48cf9ff9785e5815c134a6deca43c..9e52d5cdfe794ee5a71133633438330836f0adbf 100644 (file)
@@ -258,3 +258,89 @@ static int dm_test_net_retry(struct unit_test_state *uts)
        return retval;
 }
 DM_TEST(dm_test_net_retry, DM_TESTF_SCAN_FDT);
+
+static int sb_check_arp_reply(struct udevice *dev, void *packet,
+                             unsigned int len)
+{
+       struct eth_sandbox_priv *priv = dev_get_priv(dev);
+       struct ethernet_hdr *eth = packet;
+       struct arp_hdr *arp;
+       /* Used by all of the ut_assert macros */
+       struct unit_test_state *uts = priv->priv;
+
+       if (ntohs(eth->et_protlen) != PROT_ARP)
+               return 0;
+
+       arp = packet + ETHER_HDR_SIZE;
+
+       if (ntohs(arp->ar_op) != ARPOP_REPLY)
+               return 0;
+
+       /* This test would be worthless if we are not waiting */
+       ut_assert(arp_is_waiting());
+
+       /* Validate response */
+       ut_assert(memcmp(eth->et_src, net_ethaddr, ARP_HLEN) == 0);
+       ut_assert(memcmp(eth->et_dest, priv->fake_host_hwaddr, ARP_HLEN) == 0);
+       ut_assert(eth->et_protlen == htons(PROT_ARP));
+
+       ut_assert(arp->ar_hrd == htons(ARP_ETHER));
+       ut_assert(arp->ar_pro == htons(PROT_IP));
+       ut_assert(arp->ar_hln == ARP_HLEN);
+       ut_assert(arp->ar_pln == ARP_PLEN);
+       ut_assert(memcmp(&arp->ar_sha, net_ethaddr, ARP_HLEN) == 0);
+       ut_assert(net_read_ip(&arp->ar_spa).s_addr == net_ip.s_addr);
+       ut_assert(memcmp(&arp->ar_tha, priv->fake_host_hwaddr, ARP_HLEN) == 0);
+       ut_assert(net_read_ip(&arp->ar_tpa).s_addr ==
+                 string_to_ip("1.1.2.4").s_addr);
+
+       return 0;
+}
+
+static int sb_with_async_arp_handler(struct udevice *dev, void *packet,
+                                    unsigned int len)
+{
+       struct eth_sandbox_priv *priv = dev_get_priv(dev);
+       struct ethernet_hdr *eth = packet;
+       struct arp_hdr *arp = packet + ETHER_HDR_SIZE;
+       int ret;
+
+       /*
+        * If we are about to generate a reply to ARP, first inject a request
+        * from another host
+        */
+       if (ntohs(eth->et_protlen) == PROT_ARP &&
+           ntohs(arp->ar_op) == ARPOP_REQUEST) {
+               /* Make sure sandbox_eth_recv_arp_req() knows who is asking */
+               priv->fake_host_ipaddr = string_to_ip("1.1.2.4");
+
+               ret = sandbox_eth_recv_arp_req(dev);
+               if (ret)
+                       return ret;
+       }
+
+       sandbox_eth_arp_req_to_reply(dev, packet, len);
+       sandbox_eth_ping_req_to_reply(dev, packet, len);
+
+       return sb_check_arp_reply(dev, packet, len);
+}
+
+static int dm_test_eth_async_arp_reply(struct unit_test_state *uts)
+{
+       net_ping_ip = string_to_ip("1.1.2.2");
+
+       sandbox_eth_set_tx_handler(0, sb_with_async_arp_handler);
+       /* Used by all of the ut_assert macros in the tx_handler */
+       sandbox_eth_set_priv(0, uts);
+       sandbox_eth_skip_timeout();
+
+       env_set("ethact", "eth@10002000");
+       ut_assert(net_loop(PING) == -ETIMEDOUT);
+       ut_asserteq_str("eth@10002000", env_get("ethact"));
+
+       sandbox_eth_set_tx_handler(0, NULL);
+
+       return 0;
+}
+
+DM_TEST(dm_test_eth_async_arp_reply, DM_TESTF_SCAN_FDT);