Skip to content

IPv6 UDP send not working. #5744

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
4 of 6 tasks
oddstr13 opened this issue Feb 8, 2019 · 7 comments
Closed
4 of 6 tasks

IPv6 UDP send not working. #5744

oddstr13 opened this issue Feb 8, 2019 · 7 comments

Comments

@oddstr13
Copy link
Contributor

oddstr13 commented Feb 8, 2019

Basic Infos

  • This issue complies with the issue POLICY doc.
  • I have read the documentation at readthedocs and the issue is not addressed there.
  • I have tested that the issue is present in current master branch (aka latest git).
  • I have searched the issue tracker for a similar issue.
  • If there is a stack dump, I have decoded it.
  • I have filled out all fields below.

Platform

  • Hardware: [ESP-12]
  • Core Version: [latest git hash or date]
  • Development Env: [Arduino IDE|other]
  • Operating System: [Windows]

Settings in IDE

Settings screenshot
Relevant: lwIP variant: v2 IPv6 Lower memory

Problem Description

IPv6 UDP packets are not sent due to local_ip being set to 0.0.0.0, IPv4.
This is the underlying issue that prompted my question in regards to multicast IPv6.

For IPv6 multicast, I got it working by hacking setMulticastInterface in UdpContext.h:

     void setMulticastInterface(const IPAddress& addr)
     {
 #if LWIP_VERSION_MAJOR == 1
         udp_set_multicast_netif_addr(_pcb, (ip_addr_t)addr);
 #else
+        _pcb->local_ip.u_addr = ((const ip_addr_t*)addr)->u_addr;
+        _pcb->local_ip.type = ((const ip_addr_t*)addr)->type;
         udp_set_multicast_netif_addr(_pcb, ip_2_ip4((const ip_addr_t*)addr));
 #endif
     }

And passing the IPv6 address of the interface to beginPacketMulticast;

IPAddress getIP6Address(int ifn=STATION_IF) {
    // Prefer non-local address
    for (auto a: addrList) {
        if (a.ifnumber() == ifn && a.addr().isV6() && !a.addr().isLocal()) {
            return a.addr();
        }
    }

    // Fall back to local address
    for (auto a: addrList) {
        if (a.ifnumber() == ifn && a.addr().isV6()) {
            return a.addr();
        }
    }

    // Final fall-back to the IPv6 wildcard address; [::]
    return IP6_ADDR_ANY;
}

// -----------------------------------------------
void loop() {
  // <snip>
  Udp.beginPacketMulticast(multicast_ip, multicast_port, getIP6Address(), multicast_ttl)
  // <snip>
}

The reason it fails :ust rc=-6, is due to !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip) in lwIP's udp.c.
From <lwip/ip_addr.h>:

#define IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr) (IP_GET_TYPE(&pcb->local_ip) == IP_GET_TYPE(ipaddr))
#define IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr) (IP_IS_ANY_TYPE_VAL(pcb->local_ip) || IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr))

The local_ip is neither IP6_ADDR_ANY, or of type IPADDR_TYPE_ANY, and as such, udp_sendto fails with ERR_VAL(-6).

IP_ADDR_ANY is defined as IP4_ADDR_ANY in <lwip/ip_address.h>, which is a ip_addr_t = {u_addr={0ul, 0ul, 0ul, 0ul}, type=0.

Ideally, IP_ADDR_ANY should probably have type IPADDR_TYPE_ANY(46), not IPADDR_TYPE_V4(0) on dual-stack systems, but this might be an upstream issue?

Lastly, this issue might not show up when replying to received packets (I have not tested this).

MCVE Sketch

Traffic watched on router with tcpdump -i br-lan -n port 18888

15:13:05.245105 IP 10.79.2.187.50506 > 8.8.8.8.18888: UDP, length 7
15:13:16.262131 IP 10.79.2.187.50506 > 8.8.8.8.18888: UDP, length 7
#define LWIP_DEBUG 1
#include <lwip/debug.h>
#define UDP_DEBUG LWIP_DBG_ON
#include <lwip-git-hash.h>

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <wifi_credentials.h> // WIFI_SSID & WIFI_PASSWORD

uint16_t  remote_port = 18888;

char* remote_address_1_str = "8.8.8.8";
char* remote_address_2_str = "2001:4860:4860::8888";

IPAddress remote_address_1;
IPAddress remote_address_2;


WiFiUDP Udp;

void setup() {
  Serial.begin(115200);

  Serial.println();
  Serial.println(ESP.getFullVersion());
  Serial.println();
  
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(500);
  }
  Serial.print("Connected! IP address: ");
  Serial.println(WiFi.localIP());

  
  //Serial.printf("UDP server on port %d\n", localPort);
  //Udp.begin(localPort);
  remote_address_1.fromString(remote_address_1_str);
  remote_address_2.fromString(remote_address_2_str);
}

void loop() {
  Serial.println("----------------------------------");
  
  Serial.print("Sending UDP packet to ");
  Serial.print(remote_address_1);
  Serial.print(" port ");
  Serial.println(remote_port);

  Serial.print("Udp.beginPacket: ");
  Serial.println((int)Udp.beginPacket(remote_address_1, remote_port), DEC);

  Serial.print("Udp.write: ");
  Serial.println((int)Udp.write("Testing"), DEC);

  Serial.print("Udp.endPacket: ");
  Serial.println((int)Udp.endPacket(), DEC);
  
  Serial.println("----------------------------------");
  delay(1000);

  Serial.print("Sending UDP packet to ");
  Serial.print(remote_address_2);
  Serial.print(" port ");
  Serial.println(remote_port);

  Serial.print("Udp.beginPacket: ");
  Serial.println((int)Udp.beginPacket(remote_address_2, remote_port), DEC);

  Serial.print("Udp.write: ");
  Serial.println((int)Udp.write("Testing"), DEC);

  Serial.print("Udp.endPacket: ");
  Serial.println((int)Udp.endPacket(), DEC);
  
  delay(10000);
}

Debug Messages

SDK:3.0.0-dev(c0f7b44)/Core:2.5.0-beta3=20499903/lwIP:IPv6+STABLE-2_1_2_RELEASE/glue:1.0-11-g87c709d/BearSSL:6778687

sta configwifi evt: 2
 unchangedscandone
.scandone
state: 0 -> 2 (b0)
state: 2 -> 3 (0)
state: 3 -> 5 (10)
add 0
aid 9
cnt 

connected with Solvang, channel 6
dhcp client start...
wifi evt: 0
...ip:10.79.2.187,mask:255.255.252.0,gw:10.79.0.1
wifi evt: 3
Connected! IP address: 10.79.2.187
----------------------------------
Sending UDP packet to 8.8.8.8 port 18888
Udp.beginPacket: 1
Udp.write: 7
Udp.endPacket: 1
----------------------------------
ip:10.79.2.187,mask:255.255.252.0,gw:10.79.0.1
ip:10.79.2.187,mask:255.255.252.0,gw:10.79.0.1
Sending UDP packet to 2001:4860:4860::8888 port 18888
Udp.beginPacket: 1
Udp.write: 7
Udp.endPacket: :ust rc=-6
:: _pcb == NULL = false
:: addr == NULL = false
:: IP_ADDR_PCB_VERSION_MATCH(_pcb, addr) = false
:: _pcb->local_ip = 0.0.0.0
:: _pcb->local_ip.type = 0
:: addr = 2001:4860:4860::8888
0
pm open,type:2 0
----------------------------------
Sending UDP packet to 8.8.8.8 port 18888
Udp.beginPacket: 1
Udp.write: 7
Udp.endPacket: 1
----------------------------------
Sending UDP packet to 2001:4860:4860::8888 port 18888
Udp.beginPacket: 1
Udp.write: 7
Udp.endPacket: :ust rc=-6
:: _pcb == NULL = false
:: addr == NULL = false
:: IP_ADDR_PCB_VERSION_MATCH(_pcb, addr) = false
:: _pcb->local_ip = 0.0.0.0
:: _pcb->local_ip.type = 0
:: addr = 2001:4860:4860::8888
0
----------------------------------

Edit:
Some extra debug output in <UdpContext.h> send() is present;

         err_t err = udp_sendto(_pcb, tx_copy, addr, port);
         if (err != ERR_OK) {
             DEBUGV(":ust rc=%d\r\n", (int) err);
+            //#define IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr) (IP_GET_TYPE(&pcb->local_ip) == IP_GET_TYPE(ipaddr))
+            //#define IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr) (IP_IS_ANY_TYPE_VAL(pcb->local_ip) || IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ipaddr))
+            DEBUGV(":: _pcb == NULL = %s\r\n", (_pcb == NULL) ? "true" : "false");
+            DEBUGV(":: addr == NULL = %s\r\n", (addr == NULL) ? "true" : "false");
+            DEBUGV(":: IP_ADDR_PCB_VERSION_MATCH(_pcb, addr) = %s\r\n", (IP_ADDR_PCB_VERSION_MATCH(_pcb, addr)) ? "true" : "false");
+            DEBUGV(":: _pcb->local_ip = %s\r\n", (ipaddr_ntoa(&_pcb->local_ip)));
+            DEBUGV(":: _pcb->local_ip.type = %d\r\n", (int)(_pcb->local_ip.type));
+            DEBUGV(":: addr = %s\r\n", (ipaddr_ntoa(addr)));
         }
@d-a-v
Copy link
Collaborator

d-a-v commented Feb 8, 2019

Thanks for MCVE !
This should be fixed by #5743 (merged)
Can you try it ?

@oddstr13
Copy link
Contributor Author

oddstr13 commented Feb 8, 2019

#5743 does not touch the underlying issue of local_ip being set to 0.0.0.0 by default (the socket is not used before attempting to send a IPv6 packet).

I believe initializing pcb->local_ip with IPADDR_TYPE_ANY(46) (:: and 0.0.0.0, not 0.0.0.0 only) would solve this issue.

IP_IS_ANY_TYPE_VAL(ipaddr) from <lwip/ip_addr.h> is used in the places that matter for this particular issue.

oddstr13 added a commit to oddstr13/esp8266-Arduino that referenced this issue Feb 8, 2019
Initialize UdpContext `_pcb->local_ip.type` with `IPADDR_TYPE_ANY` in dual-stack mode.
@oddstr13
Copy link
Contributor Author

oddstr13 commented Feb 8, 2019

Please see #5745

@d-a-v
Copy link
Collaborator

d-a-v commented Feb 9, 2019

The local_ip is neither IP6_ADDR_ANY, or of type IPADDR_TYPE_ANY, and as such, udp_sendto fails with ERR_VAL(-6).

pcb->local_ip is initialized in lwIP's udp_new() which merely makes a memset(0) (initializing local_ip to IP4_ADDR_ANY).
You claim it is sufficient to set local_ip to IPADDR_TYPE_ANY in that function, which I agree.
Like you say, it would be an upstream bug that deserves fixing.

That's what you do in #5745 (your #elif will never be compiled), but that will fix only this one call to udp_new().
What do you think of adding a patch in lwip2 ?
That patch could also be proposed to upstream like @davefiddes(link) did for another issue (merged upstream).

@oddstr13
Copy link
Contributor Author

I couldn't figure out where lwip2 came from, so this was the closest I got to the issue from my position at the time (easier to fix source code compiled on every upload than precompiled libraries).

The #elif is intended for single-stack IPv6, which is currently not in use in the Arduino core.
The patch would clearly best be applied in lwip2, and preferably upstream - but I feel that savannah is rather cumbersome to navigate, and I am not familiar with the "pull-request" system there.

We should probably also check if this issue is present in TCP or other protocols where applicable.

Which ever way you choose to solve the issue, it would probably be best to get it in for the next release.

This is probably how I would've done it if I knew at the time how to get at the lwip2 source code.

Not tested, but should do exactly the same as #5745.
I can try to figure out how to test it, and submit a patch file pull-request to d-a-v/esp82xx-nonos-linklayer if you wish. Feel free to do so yourself, if that is convenient.

diff --git a/src/core/udp.c b/src/core/udp.c
index 9d2cb4af..40cdf190 100644
--- a/src/core/udp.c
+++ b/src/core/udp.c
@@ -1233,6 +1233,12 @@ udp_new(void)
 #if LWIP_MULTICAST_TX_OPTIONS
     udp_set_multicast_ttl(pcb, UDP_TTL);
 #endif /* LWIP_MULTICAST_TX_OPTIONS */
+// Defaults to IPADDR_TYPE_V4 (0)
+#if LWIP_IPV4 && LWIP_IPV6
+    pcb->local_ip.type = IPADDR_TYPE_ANY;
+#elif LWIP_IPV6
+    pcb->local_ip.type = IPADDR_TYPE_V6;
+#endif
   }
   return pcb;
 }

@d-a-v
Copy link
Collaborator

d-a-v commented Feb 10, 2019

lwip2 is ours, on my gh pages. tools/sdk/lwip2/builder is a submodule linked to it.
You can make a pull request there with your patch. Then we can import compiled libraries to here with another pull request.

To test it, put your above patch in lwip2's patches/ directory, completely remove lwip2-src/ subdir so it is re-checked-out and repatched when following the instructions.

oddstr13 added a commit to oddstr13/esp82xx-nonos-linklayer that referenced this issue Feb 11, 2019
@oddstr13
Copy link
Contributor Author

Right, sneaky submodules..
d-a-v/esp82xx-nonos-linklayer#28 tests the same as #5745

d-a-v pushed a commit to d-a-v/esp82xx-nonos-linklayer that referenced this issue Feb 11, 2019
d-a-v added a commit to d-a-v/Arduino that referenced this issue Feb 11, 2019
From: @oddstr13
(d-a-v/esp82xx-nonos-linklayer#28)

Initialize pcb->local_ip.type depending on whether we have IPv4, IPv4+IPv6 or IPv6 networking stack.

Fixes esp8266#5744
Closes esp8266#5745
d-a-v added a commit to oddstr13/esp8266-Arduino that referenced this issue Feb 11, 2019
From: @oddstr13
(d-a-v/esp82xx-nonos-linklayer#28)

Initialize pcb->local_ip.type depending on whether we have IPv4, IPv4+IPv6 or IPv6 networking stack.

Fixes esp8266#5744
Closes esp8266#5745
someburner pushed a commit to someburner/esp82xx-nonos-linklayer that referenced this issue Feb 16, 2019
…her we have IPv4, IPv4+IPv6 or IPv6 networking stack. (#28)

Fixes esp8266/Arduino#5744
Closes esp8266/Arduino#5745
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants