Skip to content
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

Add IPv6 support #82

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open

Conversation

vinicentus
Copy link

@vinicentus vinicentus commented Apr 13, 2020

I have made an effort to add IPv6 support. This might not be perfect, but it is a working (and mostly tested) implementation with examples. I'm still new to C++, so if you find any mistakes etc. feel free to point them out :) Also, I can squash the commits if you want.

The things that still need to be done are:

  • Update README
  • Add changelog
  • Make sure this works with global unicast addresses

There shouldn't be any breaking changes.

Currently, this supports IPv4-mapped IPv6 addresses, BUT it sees them as IPv4 addresses internally for some reason that I don't understand yet:
13-04-2020

resolves #39

PS. #39 (comment) helped me out quite a bit :)

@fhessel
Copy link
Owner

fhessel commented Apr 13, 2020

I just recently started to work on IPv6 support as well, I just pushed my current progress, should have updated the issue before. The main point where I'm stuck at the moment is that I get only link-local addresses working. I still needed to find out how to use DHCPv6 or SLAAC with the underlying IDF. Maybe it makes sense to join forces on this.

I'll need to check out your branch and see how you addressed this / how the mapped addresses work in the IDF.

Edit: Running the builds on PRs by non-owners doesn't seem to work, I'll fix that, don't care about the failing checks.

@fhessel fhessel added CI: Build Examples When set, the examples will be built for this PR and removed CI: Build Examples When set, the examples will be built for this PR labels Apr 13, 2020
@vinicentus
Copy link
Author

vinicentus commented Apr 13, 2020

Actually, I haven't tested with other addresses than link-local yet either, and I have a hunch that global addresses might not work yet in my implementation.

I'd be happy to work on this together. I'll check out your work soon.

@vinicentus
Copy link
Author

Here's a useful note about IPv6 and sockets https://tools.ietf.org/html/rfc3493

@fhessel
Copy link
Owner

fhessel commented Apr 13, 2020

Here's a useful note about IPv6 and sockets https://tools.ietf.org/html/rfc3493

My main problem is with the implementation in the esp-idf and the Arduino Core (which is still built on top of the older 3.x IDF). I'm currently looking for the highest-possible abstraction to register a global or unique local address to on of the interfaces of the ESP32. I didn't find any support for more than Link-Local addresses in the Arduino Framework (I had wished for WiFi::addIPv6Address(...) or something similar).

I think a major difference between our approaches is that you stick to keeping a single bind address for the server, where my implementation uses a vector for IPv4 and v6 addresses and (not yet implemented) opens a socket for each registered interface. I assume with that approach, most "if v6 do this, else to that" vanish from the code, instead its just "for all ipv4 addresses do this, for all v6 do that", and it also allows a dual stack server with different addresses by creating it and calling something like server->addAddress(...) before starting it.

@vinicentus
Copy link
Author

vinicentus commented Apr 13, 2020

As far as I can tell, arduino-esp32 is based on esp-idf v3.2.x. It seems that esp-idf v3.3.2 is the earliest version that supports IPv6 global addresses, and getting them through SLAAC. No support for DHCPv6 though.

There's also this: espressif/arduino-esp32#1261, but I think there is some misinformation in there.

So, it seems like arduino-esp should be updated to use esp-idf 3.3.x, which hopefully shouldn't be too hard?

EDIT: updated links, and added these:
https://docs.espressif.com/projects/esp-idf/en/v3.3.2/api-reference/kconfig.html#config-lwip-ipv6-autoconfig
https://docs.espressif.com/projects/esp-idf/en/v3.3.2/api-guides/wifi.html#system-event-ap-sta-got-ip6
https://docs.espressif.com/projects/esp-idf/en/v3.3.2/api-reference/network/tcpip_adapter.html#_CPPv431tcpip_adapter_get_ip6_linklocal18tcpip_adapter_if_tP10ip6_addr_t

EDIT 2:
It seems as though esp-idf v4 support is being worked on currently
espressif/arduino-esp32#3739
espressif/arduino-esp32#3670

esp-idf v4.1-beta1 is the first 4.x version to support SLAAC.

@fhessel
Copy link
Owner

fhessel commented Apr 13, 2020

It seems as though esp-idf v4 support is being worked on currently

That's why I stopped working on my branch, because I wanted to wait for that.

EDIT: updated links, and added these:
https://docs.espressif.com/projects/esp-idf/en/v3.3.2/api-reference/kconfig.html#config-lwip-ipv6-autoconfig

You cannot change compiler settings for the precompiled modules but need to live with what is preconfigured with the Arduino Core. From the Arduino perspective, the lwip stack is just an immutable blob that's linked to your application.

https://docs.espressif.com/projects/esp-idf/en/v3.3.2/api-reference/network/tcpip_adapter.html#_CPPv431tcpip_adapter_get_ip6_linklocal18tcpip_adapter_if_tP10ip6_addr_t

I found that too, that's what I meant with looking for the highest-possible abstraction, because retrieving the interface and creating/adding a ip6_addr_t is not what you want to do in an Arduino example sketch.

@vinicentus
Copy link
Author

I found that too, that's what I meant with looking for the highest-possible abstraction, because retrieving the interface and creating/adding a ip6_addr_t is not what you want to do in an Arduino example sketch.

I'm guessing they will just wrap the tcpip_adapter_get_ip6_globallike like here, which should be fine for most use cases like wifi client, wifi AP, or even ethernet. Then getting an IPv6 address by SLAAC automatically should be fine in most cases, right?

And yes, I guess we will just have to wait to get support for SLAAC.

For now though something similar to my implementation is probably ok? Although you did outdo me with the ifdefs and the use of IPAddress in constructor.

The only thing I don't quite understand from your code is what exactly the vectors for addresses would be used for? Would the server create a new socket for every call to server->addAddress(x) , adding x to the vector, and binding said socket to x?

Also, how about IPv4-mapped addresses? Should the default IPv6 server also accept IPv4 connections by use of this, or should there be two separate sockets (ipv4 +ipv6)?

@fhessel
Copy link
Owner

fhessel commented Apr 17, 2020

The only thing I don't quite understand from your code is what exactly the vectors for addresses would be used for? Would the server create a new socket for every call to server->addAddress(x) , adding x to the vector, and binding said socket to x?

As I said, it's not fully implemented. The final goal would be to create a socket for each of the addresses in the vectors instead of setting up the the single socket here. Then, in the loop() function of the server, one could select on all of these sockets and process those with data on them like before. From there it should be transparent.

You wouldn't use that feature very much, I think, but it has been an issue when running the server in AP and STA mode simultaneously (now you can only bind to all addresses or setup two server instances). And having dual stack support in a single server could be another benefit, that's why I started to implement that together with the IPv6 support. Note that after the socket is created and bind is called, there's no need to differentiate between v4 and v6 any longer, so it would really be only the connection setup which gets more complex.

Also, how about IPv4-mapped addresses? Should the default IPv6 server also accept IPv4 connections by use of this, or should there be two separate sockets (ipv4 +ipv6)?

The behavior that I would expect is that if I use a bind-all IPv6 address, I get only IPv6 traffic. Also, excluding the mapped addresses reduces conflicts with double bindings if multiple sockets are created. So if you want IPv4 and v6, you should use a bind-all address for each protocol (so IPV6_V6ONLY by default).

So how to proceed... I could create a PR on top of yours that merges both approaches. Then you could have a look at it and tell me your opinion about that. And for actual IPv6 operation, we'll wait for the Arduino Core to be updated to the latest IDF and see how it turns out?

@vinicentus
Copy link
Author

vinicentus commented Apr 21, 2020

As I said, it's not fully implemented. The final goal would be to create a socket for each of the addresses in the vectors instead of setting up the the single socket here. Then, in the loop() function of the server, one could select on all of these sockets and process those with data on them like before. From there it should be transparent.

Ok, I understand now.

You wouldn't use that feature very much, I think, but it has been an issue when running the server in AP and STA mode simultaneously (now you can only bind to all addresses or setup two server instances). And having dual stack support in a single server could be another benefit, that's why I started to implement that together with the IPv6 support. Note that after the socket is created and bind is called, there's no need to differentiate between v4 and v6 any longer, so it would really be only the connection setup which gets more complex.

Seems like a good idea. It should be useful in some cases.

The behavior that I would expect is that if I use a bind-all IPv6 address, I get only IPv6 traffic. Also, excluding the mapped addresses reduces conflicts with double bindings if multiple sockets are created. So if you want IPv4 and v6, you should use a bind-all address for each protocol (so IPV6_V6ONLY by default).

That definitely makes sense. But how would the user specify that they want to use both ipv6 and v4? I still think it it should be done in the constructor, but would it be a bool flag or would they need to pass two bind addresses? The point I'm trying to make here is that it should be easy to support both protocols, without any extra setup.
Then we should also allow them to disable the IPV6_V6ONLY flag, but that could be a call to server.enableMappedAddress() or similar.

EDIT: There could be a constructor that binds to all addresses and takes an enableIpv6 flag. But when you want to use custom addresses, you might still need to pass one address in the constructor and then use your suggested server->addAddress(x) for the other one (ipv4 and ipv6 order shouldn't matter).

So how to proceed... I could create a PR on top of yours that merges both approaches. Then you could have a look at it and tell me your opinion about that. And for actual IPv6 operation, we'll wait for the Arduino Core to be updated to the latest IDF and see how it turns out?

Fantastic! I'd like that.

@vinicentus
Copy link
Author

Is there any update or progress on this?

@fhessel
Copy link
Owner

fhessel commented Jun 6, 2020

Sorry, I've been quite busy the last weeks, I'll try to get to this soon.

(However, also nothing new for the Arduino Core, though 😐 )

@vinicentus
Copy link
Author

Yeah. No worries. It might be possible to test against this branch, though I'm not sure.

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

Successfully merging this pull request may close these issues.

IPV6 Webserver.
2 participants