diff mbox series

[U-Boot,08/10] test: eth: Add a test for ARP requests

Message ID 20180724214012.22798-9-joe.hershberger@ni.com
State Superseded
Delegated to: Joe Hershberger
Headers show
Series net: Fix packet corruption issue when handling asynch replies | expand

Commit Message

Joe Hershberger July 24, 2018, 9:40 p.m. UTC
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>
---

 arch/sandbox/include/asm/eth.h |  9 +++++
 drivers/net/sandbox.c          | 41 +++++++++++++++++++++
 test/dm/eth.c                  | 81 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 131 insertions(+)

Comments

Simon Glass Aug. 2, 2018, 5:08 p.m. UTC | #1
On 24 July 2018 at 15:40, Joe Hershberger <joe.hershberger@ni.com> wrote:
> 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>
> ---
>
>  arch/sandbox/include/asm/eth.h |  9 +++++
>  drivers/net/sandbox.c          | 41 +++++++++++++++++++++
>  test/dm/eth.c                  | 81 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 131 insertions(+)

Nice!

Reviewed-by: Simon Glass <sjg@chromium.org>
Bin Meng Sept. 25, 2018, 8:22 a.m. UTC | #2
Hi Joe,

On Wed, Jul 25, 2018 at 5:43 AM Joe Hershberger <joe.hershberger@ni.com> wrote:
>
> 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>
> ---
>
>  arch/sandbox/include/asm/eth.h |  9 +++++
>  drivers/net/sandbox.c          | 41 +++++++++++++++++++++
>  test/dm/eth.c                  | 81 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 131 insertions(+)
>

Reviewed-by: Bin Meng <bmeng.cn@gmail.com>

But please see comments below:

> diff --git a/arch/sandbox/include/asm/eth.h b/arch/sandbox/include/asm/eth.h
> index 6bb3f1bbfd..7cd5b551d9 100644
> --- a/arch/sandbox/include/asm/eth.h
> +++ b/arch/sandbox/include/asm/eth.h
> @@ -33,6 +33,15 @@ 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
> + *
> + * returns 1 if injected, 0 if not

again the return codes, can we use -Exxx instead? I see the return
error is not checked anywhere, maybe we should just make it return
void?

> + */
> +int sandbox_eth_recv_arp_req(struct udevice *dev);
> +
>  /**
>   * A packet handler
>   *
> diff --git a/drivers/net/sandbox.c b/drivers/net/sandbox.c
> index 29767ef291..2c2a2c6311 100644
> --- a/drivers/net/sandbox.c
> +++ b/drivers/net/sandbox.c
> @@ -161,6 +161,47 @@ int sandbox_eth_ping_req_to_reply(struct udevice *dev, void *packet,
>  }
>
>  /*
> + * sandbox_eth_recv_arp_req()
> + *
> + * Inject an ARP request for this target
> + *
> + * returns 1 if injected, 0 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 0;
> +
> +       /* 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 1;
> +}
> +
> +/*
>   * sb_default_handler()
>   *
>   * perform typical responses to simple ping
> diff --git a/test/dm/eth.c b/test/dm/eth.c
> index 1a7684a887..9b5f53e819 100644
> --- a/test/dm/eth.c
> +++ b/test/dm/eth.c
> @@ -258,3 +258,84 @@ 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;
> +       struct unit_test_state *uts = priv->priv;

This is not used anywhere. What is this for?

> +
> +       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;
> +
> +       /*
> +        * 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");
> +
> +               sandbox_eth_recv_arp_req(dev);
> +       }
> +
> +       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);
> +       sandbox_eth_set_priv(0, uts);

Looks this is not needed since it was not used anywhere.

> +       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);
> --

Regards,
Bin
diff mbox series

Patch

diff --git a/arch/sandbox/include/asm/eth.h b/arch/sandbox/include/asm/eth.h
index 6bb3f1bbfd..7cd5b551d9 100644
--- a/arch/sandbox/include/asm/eth.h
+++ b/arch/sandbox/include/asm/eth.h
@@ -33,6 +33,15 @@  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
+ *
+ * returns 1 if injected, 0 if not
+ */
+int sandbox_eth_recv_arp_req(struct udevice *dev);
+
 /**
  * A packet handler
  *
diff --git a/drivers/net/sandbox.c b/drivers/net/sandbox.c
index 29767ef291..2c2a2c6311 100644
--- a/drivers/net/sandbox.c
+++ b/drivers/net/sandbox.c
@@ -161,6 +161,47 @@  int sandbox_eth_ping_req_to_reply(struct udevice *dev, void *packet,
 }
 
 /*
+ * sandbox_eth_recv_arp_req()
+ *
+ * Inject an ARP request for this target
+ *
+ * returns 1 if injected, 0 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 0;
+
+	/* 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 1;
+}
+
+/*
  * sb_default_handler()
  *
  * perform typical responses to simple ping
diff --git a/test/dm/eth.c b/test/dm/eth.c
index 1a7684a887..9b5f53e819 100644
--- a/test/dm/eth.c
+++ b/test/dm/eth.c
@@ -258,3 +258,84 @@  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;
+	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;
+
+	/*
+	 * 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");
+
+		sandbox_eth_recv_arp_req(dev);
+	}
+
+	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);
+	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);