Why doesn't LWIP Respond to ARP Requests

jhinkle
Posts: 35
Joined: Wed Aug 29, 2018 3:17 pm

Why doesn't LWIP Respond to ARP Requests

Postby jhinkle » Fri Mar 28, 2025 8:19 pm

Thank goodness I have the IDF set to issue a gratuitous ARP every so often.

As you can see below - I have a desktop Chrome Browser attempting to open a web page on my ESP HTTP server. Chrome starts requesting ARP responses but LWIP provides nothing. Finally LWIP issues a gratuitous ARP and the web pages load.

SO ... How do you tell the IDF or LWIP to respond to ARP requests?

I'm using a static IP if that helps.

Image

Sprite
Espressif staff
Espressif staff
Posts: 10596
Joined: Thu Nov 26, 2015 4:08 am

Re: Why doesn't LWIP Respond to ARP Requests

Postby Sprite » Sat Mar 29, 2025 6:46 am

If you don't enable gratuitous ARP, what happens? I'm thinking you might be seeing the effect of WiFi power saving here, which makes the ESP32 not listen (with the AP buffering the packets for it) for small stretches of time.

jhinkle
Posts: 35
Joined: Wed Aug 29, 2018 3:17 pm

Re: Why doesn't LWIP Respond to ARP Requests

Postby jhinkle » Sat Mar 29, 2025 11:31 am

ESP_Sprite:

Went into IDF Config and turned off gratuitous ARP and made sure all power management options are OFF/Not selected.

Compiled -- The browser NEVER connects with the Web Server -- still no ARP reply from LWIP.

I've gone back in and enabled gratuitous ARP and changed time from 60 secs to 15.

From your reply, it sounds like you suspect this behavior is the result of some other option/capability getting in the way.

Thoughts? -- Thanks.

jhinkle
Posts: 35
Joined: Wed Aug 29, 2018 3:17 pm

Re: Why doesn't LWIP Respond to ARP Requests

Postby jhinkle » Sat Mar 29, 2025 12:03 pm

As stated in my previous post - I set gratuitous ARP timing to 15 secs and unchecked ESP_WIFI_STA_DISCONNECTED_PM_ENABLE

Once a gratuitous ARP appeared - the Web Server was able to talk. THEN -- everything STOPPED. See the wireshark below. Once the gratuitous ARP STOPPED being sent - the Web Server died (This is my own web server code - NOT the web server from the IDF components.

Thoughts?

Image

Sprite
Espressif staff
Espressif staff
Posts: 10596
Joined: Thu Nov 26, 2015 4:08 am

Re: Why doesn't LWIP Respond to ARP Requests

Postby Sprite » Sun Mar 30, 2025 6:48 am

Odd. Can you explain a bit more about your setup: what hardware, what ESP-IDF, are you using a different SDK as well? Furthermore, can you reproduce the issue with e.g. the ESP-IDF webserver example?

jhinkle
Posts: 35
Joined: Wed Aug 29, 2018 3:17 pm

Re: Why doesn't LWIP Respond to ARP Requests

Postby jhinkle » Sun Mar 30, 2025 10:17 am

ESP_Sprite:

Hardware ESP32 WROOM - two core

Web server - my design I been using in STM cpus for 10 years - uses sockets. NOT LWIP - I use FreeRTOS +TCP.

Using a JTAG debugger, I trapped the msg packet when it came in from wifi and watched how it was processed. I found that once the code was interrupted (breakpoint) the ESP would stop processing packets. I would have to reset, and watch a single trap again.

By using JTAG to watch the flow I went into the IDF source and added my own logging to watch flow and variables (logging did not freeze the ESP). The only thing I found which I could not explain was in the etharp.c file. The function processes the incoming packet.

Code: Select all

void
etharp_input(struct pbuf *p, struct netif *netif)
{
  struct etharp_hdr *hdr;
  /* these are aligned properly, whereas the ARP header fields might not be */
  ip4_addr_t sipaddr, dipaddr;
  u8_t for_us, from_us;

  //ESP_LOGI("HinkleDBA", "etharp_input\n");

  LWIP_ASSERT_CORE_LOCKED();

  LWIP_ERROR("netif != NULL", (netif != NULL), return;);

  hdr = (struct etharp_hdr *)p->payload;

  /* RFC 826 "Packet Reception": */
  if ((hdr->hwtype != PP_HTONS(LWIP_IANA_HWTYPE_ETHERNET)) ||
      (hdr->hwlen != ETH_HWADDR_LEN) ||
      (hdr->protolen != sizeof(ip4_addr_t)) ||
      (hdr->proto != PP_HTONS(ETHTYPE_IP)))  {
    LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
                ("etharp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
                 hdr->hwtype, (u16_t)hdr->hwlen, hdr->proto, (u16_t)hdr->protolen));
    ETHARP_STATS_INC(etharp.proterr);
    ETHARP_STATS_INC(etharp.drop);
    pbuf_free(p);
    return;
  }
  ETHARP_STATS_INC(etharp.recv);

#if LWIP_ACD
  /* We have to check if a host already has configured our ip address and
   * continuously check if there is a host with this IP-address so we can
   * detect collisions.
   * acd_arp_reply ensures the detection of conflicts. It will handle possible
   * defending or retreating and will make sure a new IP address is selected.
   * etharp_input does not need to handle packets that originate "from_us".
   */
  acd_arp_reply(netif, hdr);
#endif /* LWIP_ACD */

  /* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support compilers without
   * structure packing (not using structure copy which breaks strict-aliasing rules). */
  IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr);
  IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr);

  /* this interface is not configured? */
  if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
    for_us = 0;
    from_us = 0;

      //ESP_LOGI("HinkleDBA", "etharp_input - ip4_addr_isany_val(*netif_ip4_addr(netif) results Zero for_us, from_us\n" );
  } else {

    //ESP_LOGI("HinkleDBA", "etharp_input - dipaddr = %X", (unsigned int)dipaddr.addr );

    /* ARP packet directed to us? */
    for_us = (u8_t)ip4_addr_eq(&dipaddr, netif_ip4_addr(netif));
    /* ARP packet from us? */
    from_us = (u8_t)ip4_addr_eq(&sipaddr, netif_ip4_addr(netif));
  }

      //ESP_LOGI("HinkleDBA", "etharp_input for_us %d  from_us %d\n", for_us, from_us);

  /* ARP message directed to us?
      -> add IP address in ARP cache; assume requester wants to talk to us,
         can result in directly sending the queued packets for this host.
     ARP message not directed to us?
      ->  update the source IP address in the cache, if present */
  etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
                          for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);

  //ESP_LOGI("HinkleDBA", "etharp_input hdr->opcode %d  PP_HTONS(ARP_REQUEST) %d\n",hdr->opcode, PP_HTONS(ARP_REQUEST));

  /* now act on the message itself */
  switch (hdr->opcode) {
    /* ARP request? */
    case PP_HTONS(ARP_REQUEST):
      /* ARP request. If it asked for our address, we send out a
       * reply. In any case, we time-stamp any existing ARP entry,
       * and possibly send out an IP packet that was queued on it. */

       //ESP_LOGI("HinkleDBA", "etharp_input case PP_HTONS(ARP_REQUEST): for_us %d  from_us %d\n", for_us, from_us);

      LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: incoming ARP request\n"));
      /* ARP request for our address? */
      if (for_us && !from_us) {

        //ESP_LOGI("HinkleDBA", "Send etharp_raw\n");
        /* send ARP response */
        etharp_raw(netif,
                   (struct eth_addr *)netif->hwaddr, &hdr->shwaddr,
                   (struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif),
                   &hdr->shwaddr, &sipaddr,
                   ARP_REPLY);

                   //ESP_LOGI("HinkleDBA", "etharp_input Return from etharp_raw\n");
                   
        /* we are not configured? */
      } else if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
        /* { for_us == 0 and netif->ip_addr.addr == 0 } */
        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: we are unconfigured, ARP request ignored.\n"));
        /* request was not directed to us */
      } else {
        /* { for_us == 0 and netif->ip_addr.addr != 0 } */
        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP request was not for us.\n"));
      }
      break;
    case PP_HTONS(ARP_REPLY):
      /* ARP reply. We already updated the ARP cache earlier. */
      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: incoming ARP reply\n"));
      break;
    default:
      LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP unknown opcode type %"S16_F"\n", lwip_htons(hdr->opcode)));
      ETHARP_STATS_INC(etharp.err);
      break;
  }
  /* free ARP packet */
  pbuf_free(p);
}
I left my logging code in (commented out) so you can see the flow I was looking at. Notice the switch where the type of response is decided.

the flow would Enter the switch with opcode ARP_REQUEST. for_us == 1 and to_us == 0; It would enter the proper case block - pass the for_us/to_us check but quite often NEVER get to etharp_raw(); Logging would not show processing etharp_raw(). I can't explain as there is no code to change the process path - so maybe an interaction within ESP prevented the logging from being sent.

I tried several changes to the IDF via your menu.

Gratuitous ARP OFF - nothing worked then.

Gratuitous ARP ON - timer 15 sec and WIFI power management off - did not work.

I'm back at Gratuitous ARP ON - 60 sec on timer and WIFI power management on (I forgot the flag name).

Every change - I deleted the build folder and recompiled the IDF.

It is working now and I can't explain why.

I compared the sdkconfig file from before (when at timer 15 and WIFI PM off) to timer 60 and WIFI PM on - to see if there were any unseen flags being set/cleared in the background - it was OK.

I did notice one thing the I think should be addressed in the IDF config. IMO, configuration flags that control behavior should only appear once. If you look at your Gratuitous ARP timer config setting - it appears twice under two different names - one for ESP and one for LWIP - same value ... so which one does the IDF use?

I say it working fine now - but that statement is based on minimal testing. Time will tell.

Thanks for replying to my questions.

Who is online

Users browsing this forum: Semrush [Bot] and 5 guests