-
Notifications
You must be signed in to change notification settings - Fork 7.4k
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
Inconsistency in station local address functions IPv4 vs IPv6 (alpha 3) #9144
Comments
@me-no-dev Please take a look as you are working on the network refactoring. |
Nope! I can somewhat agree to the idea that You still should be able to get the IPv6 info without having to revert to ESP-IDF functions. |
I do not agree that anything needs changing. Interface can have different IPs, so we allow them to be queried. Sockets have specific IPs. |
Rather use an enum type.
And this does work perfectly fine. |
I guess I'm asking for two things here, one I think I have agreement and the other not. (1) usage of the term local (and global) #1 is the more important item. For #2 for your local address, having separate methods for IPv6 and IPv4 is acceptable. They are not of a lot of use, except for diagnostics, and then you often want to separate out nice headings for them. So if people want to keep localIP() and localIPv6() separate that may be okay. However for #1, the usage of globalIPv6 and then saeparate localIPv6 to mean link-local is just wrong. localIPv6() should work like localIP() and give you the IPv6 address (a global one if available), of the local station. (If you do want separate methods to give you global and link-local of the local station, then call then globalIPv6() and linkLocalIPv6() ... don't use localIP() and localIPv6() to mean different things for local. Note that localIP(), the IPv4 version, actually gives you a global address). The integer parameter (default 0) wasn't meant to be an enum or preferred indicator, but an index, because you may have, for example, 3 IPv6 addresses, so localIPv6(0) would get you the first, localIPv6(1) the second, and locaIPv6(2) the third. Calling localIPv6(3) or more would return empty. This is similar to how dnsIP(int) works. The ordering of the source addresses, even if limited to IPv6 only, should ideally follow RFC 6724. Even if you don't follow this ordering, you should still be able to access all the IPv6 addresses. |
The last part of your remark does illustrate why it doesn't make things more clear. For IPv4 there isn't really a difference between global and local IP. Sure some ranges will not ever be routed to the internet and there is also a range for "zeroconf" IPv4.
But for diagnostics and some specific use cases where you really want to get the IPv4 address, you should still have some function to get the IPv4 address. For this I don't want to loop through several function calls until I have the IPv4 type. Like I said, I do understand why it might be useful to have a single function to return an IP address based on the preferred type. |
There seems to be a lot of confusion here. Location of address:
Categories of addresses (applies to both IPv6 and IPv4)
An IPv4 connection between two nodes might use link-local addresses, your "local link-local" address might be 169.254.0.1 which connects to a "remote link-local" address of 169.254.0.2. From the other nodes point of view local & remote are reversed (but both are still link-local). There appears to be some confusion of using the word local as a short hand reference to link-local, which is just confusing. |
I didn't think anyone would still actually be using those 169.254 addresses. Just to be sure, I checked several of my machines here (Windows/Linux) and none is using 169.254.x.x Anyway, like I said I can understand the desire to have a single function to get the preferred IP type of your host's IP-address. (deliberately not calling it 'local' to avoid confusion here) With SPI Ethernet devices, you can have multiple Ethernet interfaces (even with the espressif/arduino-esp32 code) and of course you have WiFi which can have an IP for STA and for AP mode. So how would those be specified when querying? Also which is the "preferred" interface? What IP-address from which interface would you expect when calling |
Sorry. Maybe you meant "Most local IPv4 addresses are global scope addresses", which is true, but has nothing to do with being local. Because "Most remote IPv4 addresses are global scope addresses" is also true. So there are a lot of global IPv4 addresses that are not local -- they are remote. This is just because most IPv4 addresses (either local or remote) are global scope addresses. Link-local scope IPv4 addresses (169.254.0.0/16) are uncommon to see because the standard says don't assign one if you have DHCP, and if you do have one and receive DHCP then replace it. Unlike IPv6 where you keep (rather than replace) your link-local scope addresses when you get a global scope addresses. By default link-local IPv4 addresses are also disabled in LWIP.
But you should call it local here. The word local (by itself) means your host's IP address.
This is where the word local (by itself) is being used inconsistently. Maybe you meant "functions to get link-local scope and global scope IP addresses". In my screen shot above:
I.e. an address can be both for the local host and have a global scope. Current the IPv4 function:
Current the way the IPv6 functions are named:
Using To be able to get all IPv6 addresses, I propose an index, similar to dnsIP(), so:
The order of the above addresses is based the section 2.4 precedence table of RFC 6724. |
There is nothing wrong with a local host having a global scope address -- in fact normally this is the case, e.g. my local host has lots of global scope addresses: I'm not comfortable with the word preferred, even if I used it in the past, I didn't mean anything to do with user selected preference. There is a term in RFC 6724 for precedence. The main purpose for showing a local host address I presume is so it can be display and used by another system as a destination address (another use would be for examining logs). The main criteria here is appropriate scope; in the general case of a global scope connection this means prioritise global scope over link-local scope (both addressess will work fine in the local host network). I'm happy with the discussion of separate discussions for IPv4 vs IPv6, and will update the suggestion above. |
I assume the purpose of this issue is to make it more clear to users right? But when I look at your last proposed function call examples, I'm completely lost as to me it makes absolutely no sense to call the function I still don't get the semantics here and why this is needed to make it so complicated. You could perhaps introduce some enum to make it absolutely clear there are only 3 types you can query:
But using numbers is confusing and really hard to read the code. Also comparing the index here to the index used for DNS servers is confusing as for DNS servers there is not really a difference apart from maybe a preferred order. Just wondering; Then there is something else to consider here... |
It sounds to me like you expect the function But you expect That is an inconsistent interpretation of local, the first being local meaning link-local scope, and the second being local host scope.
I have a network configured where the two configured DNS servers are, in precedence order,
It should have no effect on security; I don't understand what your security concerns are. What are you using the returned value for? Display to the user to connect to (e.g. with a browser) -- most browsers don't understand link-local addresses? Putting into logs for troubleshooting? I think here you are using the word local in a third meaning, to mean IPv4 private address range; note that the IPv4 private address range is still considered global scope. They also are not limited to local-host network, as an organisation may have multiple connected private address ranges. The nature of private address ranges means that to allow incoming connections requires explicit set up of a port or host mapping, whereas allowing incoming access to IPv6 requires opening a firewall which may be easier to make a mistake with? If that is the case, then use the correct term private address ranges. |
I have updated the original text with a new suggestion that keeps IPv4 and IPv6 separate, but still tries to fix the inconsistent naming. I presume you mean the function I have now called
The indexes are not an enum of types (like 0 = global scope), but a precedence order, just like DNS.
My thoughts: Assuming the purpose is diagnostics, i.e. you want to look in logs and traces, then the software UI should display all IP addresses of the device. Usually this is separate labels for IPv4 and IPv6, which is why I am okay with separate functions. The IPv4 label should show the The IPv6 label should show a list of addresses from Another purpose might be for showing the address to connect to, e.g. a web browser config. For this, 2 labels, or even just 1 label, is sufficient. With 2 labels, IPv4 should show If there is no global scope IPv6 (or ULA), then
Yes, because link-local has an autoconfigure mechanism, whereas prefixed addresses need to wait to receive RA (router advertisement) with the prefix details. Both the link-local scope and global scope addresses are local host addresses. The IPv4 you get from DHCP also needs to wait for DHCP responses to be received, and is usually a global scope address (for the local host). |
The point I am trying to make here is that in order to get the link-local IP, you need to iterate over all IPs to finally get the one you need. As I mentioned before, I totally get it when you have a single function (maybe even without the index parameter, not even have it default to 0) to get the IP-type with the highest precedence. About the security aspect.... Anyway, after a night sleep, I think I now somewhat understand what your intention is with this issue. |
Why would you want to do this? What is the use case for wanting to specifically know a link-local scope address. They are largely useless for a user (they are great for automated functions) -- a global scope address can do everything a link-local can and is in general more usable, e.g. browsers don't generally support link-local addresses. If you don't have a global scope address, sure a link-local is better than nothing (but that is what you would get anyway, if they are ranked). I am also not opposed to alternative solutions, just mostly to solve the inconsistent naming. You could use:
Just don't use different meanings of local, i.e. don't use
Showing the more useful IPv6 address first doesn't help or hinder the fact that the address is still there. (Obscurity is not a good form of security). If the unit is exposed (badly configure IPv6 firewall or IPv4 NAT forwarding) then the ordering of address results doesn't affect that at all. Also, what not apply this to IPv4, i.e. enable link-local IPv4 in LWIP and use that?
You can always read the specifications to clarify what you know.
The link-local address is formed by combing fe80:: with the interface identifier, which could be one of the methods above. Yes, MAC is common, but not the only way. It is also possible to assign static link-local addresses, e.g. it is common to assign fe80::1 to a gateway router on the downstream interfaces, which makes for easy mass configuration. e.g. I have my home router set up this way. Note that LWIP only supports RA (Router Advertisement), and not DHCPv6 stateful, so all other addresses are based on the same interface identifier. i.e. global scope addresses are (in LWIP) always based on the MAC as well. Router advertisements are discussed in RFC4861 However one important thing to note is that most browsers do not support link-local addresses, so it is often useless for web-based configuration. Other tools, e.g. ping, may work, but for a common scenario of web based configuration in an IPv6 environment you need to use a global scope address (e.g. a global delegated address or ULA prefix). |
Which browsers don't support link-local addresses? |
No current browsers properly support link-local addresses; in particular they do not support specifying zone id. Here is the bug for Firefox, opened 12 years ago, with the latest comment, from 2 days ago, saying they still won't support it (basically because the RFC to specify URLs is broken and hasn't been fixed/agree upon yet, for many years; Chrome has a similar bug). https://bugzilla.mozilla.org/show_bug.cgi?id=700999 Windows, but not Linux, supports an optional part of the link-local specification for a default zone id. So, in a browser on Windows if you enter a link-local address without the scope id, then the browser will pass it to the networking subsystem no problem. If the device you want happens to be on the default zone, then it will work, because Windows supports a default zone id, not because the browser supports it. If you have 2 or more zones (2 or more network interfaces on your machine) and the device you want is not on the default zone, then it won't work. It won't work on Linux either (because Linux doesn't support default zones). Just count yourself lucky, but realise that in many other situations link-local won't work. Using a global scope address, such as a ULA, is a much better solution. Like using a 192.168.0.0 address in IPv4, rather than 169.254.0.0 address -- no one uses the 169.254 addresses, they all use 192.168 addresses. The closest thing in IPv6 is a ULA, and is fully supported (unlike link-local). e.g. if you are setting up a access point then for IPv4 you might advertise a 192.168.0.0 range via DHCP and tell users to connect to the AP, get an address from DHCP and then enter http://192.168.0.1/ in their browser to access the web interface. In IPv6 you would do similar, but use a ULA. Set up your access point with a static ULA like fd00::1, advertise the fd00:: prefix via RA (Router Advertisement), tell users to connect to the AP, they will get an address from RA, and then enter http://[fd00::1]/ in their browser to connect to the web interface. This will work on on both Linux and Windows, Android phones, etc, and irrespective of if you have one network interface or many. If you have lots of devices and want to clearly distinguish them, e.g. by scanning a QR code, then you can use a full random ULA, along with the MAC, and get a long address like http://[fd7c:e25e:67e8:40:d9f6:2780:3f9e:176b]/ that you then encode into a QR code link. Depends if you want a generic easy to enter value (like IPv4. actually shorter), or a unique value. |
But does the current Espressif implementation support ULA addresses? Also is there a way to prevent probing for a global scope address in Arduino/Espressif code? |
A ULA is just a particular group of IPv6 addresses. I have a ULA prefix advertised on my local network and did managed to get dual stack station working on my M5Stack ESP32 and it receives the prefix (and generates the address -- it doesn't treat it any different than other prefixes). Single stack IPv6-only didn't fully work (DNS issues) but it also got the ULA. I have got ESP-IDF working with IPv6-only and DNS (and ULA). For AP mode if you are setting a static address, well it is static so you just set what you want. e.g. rather than random you could use a simple fixed address; "fd00::1" or "fd00:1::1" is like picking 192.168.0.1 for IPv4.
I haven't tried this either. Should be relatively straight forward to set the static address (and work as a station), but for an AP you would want to also send RA messages with the prefix (similar to DHCP for an IPv4 station) -- I don't know if this is supported yet.
I don't know about Arduino, but there is an ESP-IDF for autoconfigure, which is listening to the RA messages and then generating based on received prefixes. But I don't think there is a way to say listen to ULA prefixes but ignore other global ones. (Technically ULAs are still global scope, but systems like RFC 6724 give the range a lower precedence). But generally you woudn't want to do this. You want a device to adapt to and work with whatever network you connect it to (without having to recompile). e.g. my mobile phone will connect to an IPv4-only network, dual stack, IPv6-only, etc, and simultaneously support all the different configurations and just use what is available. (Although Android does not support DHCPv6 Stateless, so if you want IPv6-only DNS to work you need to use RDNSS -- my laptop supports both DHCPv6 Stateless and RDNSS, so will work when either, or both, are available). |
For sure I don't want to recompile for some networks. The reason I asked about the ULA is that it seems to be the better option for link-local scope, but since I don't have them on my network, it seems like you might need to make changes to your network to make it work (or at least make it routable in your network across interfaces). |
Disabling at runtime might make sense. On a phone/laptop/etc you probably wouldn't want to do this often, but you certainly can go into Windows/Linux/etc and disable IPv4 / IPv6, etc, override DNS, and so on. You can't decide which RA prefixes to ignore (or DHCP assignments), but you can turn off automatic assignment and use a static address instead; i.e. use automatic RA for initial setup, then set a fixed static address with ULA only to override. For an IoT device it makes sense to be able to lock it down after install if you really want to, i.e. if there is no IPv4 then turn it off, etc. But it would be an advanced configuration, and for consumer devices you need a way to reset.
ULA address are global scope addresses. Maybe you meant a better option than link-local? Certainly if a device is running as a temporary AP for configuration then ULA would be the thing to use. To assign ULA addresses you just pick a range and configure it on your router, but your router needs to support it. Like IPv4, if you don't have a DHCP service on your router you won't get them; and you can pick whatever range you want and configure DHCP for it, e.g. 192.168.0.0 range, or 10.0.1.0 range, etc. Same with IPv6 Router Advertisement, although if you were configuring a router (rather than a temporary AP), I would pick a full random ULA prefix. On my router, which is running OpenWrt, the setting is in Network > Interfaces > Global network options > IPv6 ULA-Prefix |
Well the whole idea is that users should not need to adapt settings to their network to make things work. Also ULA addresses should not be routable from the internet, right? |
But kind of getting off topic here. The point of this issue was the inconsistent naming between Assuming
Then to get the link-local address, if you want a specific function for it, you could use Although I'm not sure of the use case for these; for diagnostics they are incomplete, and if it was for showing an address to connect to then the global one would generally be more useful but not guaranteed to be there. You may still want a function, e.g. (just to use different name) |
I think the default options are fine for security. Even using a delegated range, the firewall on your router will by default prevent incoming connections, but allow outgoing. The same with NAT44 for IPv4; outbound connections from your devices will be allowed and incoming not (because by default there is no port mapping; although things like UPnP or other NAT traversal mechanisms are available). e.g. Your mobile phone, or your laptop, will just connect to your network and get global addresses, but are safe. If it wasn't safe, accessing your phone is probably more valuable than your light switch or temperature sensor. For the average home tinkerer, their default router config is secure enough. I guess if you write an open IoT device, with no security itself, that just accepts any message it receives, then it is risky (if your router firewall was messed with) ... but that is risky anyway, as anyone one the local network can access it. Best to use a framework that has built in secure access, e.g. Matter. Any determined attack probably isn't going to be stopped by using a private addresses / ULA (i.e. addresses only on your local networks) -- if they compromise your router they would just use it as a jump box anyway, and you just have a false sense of security (look up "eggshell security"). |
Will be covered in #8760 |
Naming conflict is now fixed (in the networking PR branch). New functions use linkLocalIPv6(), and globalIPv6(), each of which has a clear meaning (as scopes) and is less likely to be confused with localIP() (which has a global scope address, i.e. is the IPv4 equivalent of globalIPv6()). |
Note: new naming is for the new APIs. For compatibility, we have left the old APIs mostly intact. New APIs apply to |
@sgryphon can we close this? |
Yes, happy to close this. |
Board
m5stack-core2
Device Description
M5Stack Core2 ESP32 IoT Development Kit for AWS IoT Kit, https://shop.m5stack.com/products/m5stack-core2-esp32-iot-development-kit-for-aws-iot-edukit?variant=37687799251116
Hardware Configuration
Just the M5Stack Core2
Version
latest master (checkout manually)
IDE Name
PlatformIO
Operating System
Ubuntu 22.04
Flash frequency
240Mhz
PSRAM enabled
yes
Upload speed
115200
Description
Issue
The
localIPv6()
andglobalIPv6()
accessors inWiFiSTA.h
are implemented inconsistently with howlocalIP()
is implemented, and howlocalIP()
andremoteIP()
are used inWiFiClient.h
.What should happen
globalIPv6()
should be removedlocalIP()
should continue to return the local host global scope IPv4 addresslocalIPv6()
should change to be consistent and return the local host global scope IPv6 address, if availablelocalIPv6()
should return (in priority order) a local host ULA IPv6 address, or a local host link-local scope IPv6 address.localIP()
which may return the local host link-local scope IPv4 address (if there is no global scope IPv4 and link-local IPv4 is enable in LWIP)Mechanism for additional IPv6 addresses
Ideally:
IPAddress localIPv6(uint8_t address_no = 0)
, similar todnsIP(dns_no)
.0
(i.e. just defaultlocalIP()
) returning global scope if available1
,2
, etc returning subsequent addressesa. Single global scope IPv6 address,
0
= global scope address,1
= link-local scope addressb. No global scope IPv6 address,
0
= link-local scope addressAlternative:
globalScopeIPv6()
to return a global scope IPv6 (same as current globalIPv6())linkScopeIPv6()
to return a link-local scope IPv6 (same as current localIPv6())Sketch
Debug Message
Other Steps to Reproduce
Background
The new (alpha 3) code in
WiFiSTA.h
header has several accessors to get the different IP addresses of the local station:This is only alpha, so it would be good to see the naming here fixed before release.
However the usage of the terms here is inconsistent, with some confusion between local as opposed to remote and the concept of link-local addresses.
For comparison
WiFiClient.h
header (andWiFiUdp.h
) has the following accessors, representing a connection wherelocalIP()
is the local end andremoteIP()
is the remote end:As IPAddress now supports IPv6, these connection address could be a local, and remote, IPv6 address, or IPv4.
The
WiFiSTA.h
accessor oflocalIP()
is similar, in that it represents the local (as opposed to remote), usually an IPv4 private network address, e.g.192.168.0.x
. In the current implementation it is limited to IPv4 addresses.Whilst it happens to usually be a private address range, a company with a public IPv4 address allocation could use it for WiFi, and the LWIP option
CONFIG_LWIP_AUTOIP
could also allow the device to self-assign an address in the IPv4 local-link range169.256/16
range (see RFC 3927).So
localIP()
could be a private IPv4 address, global IPv4 address, or a link-local IPv4 address.However
localIP()
does not give you IPv6 addresses, even if they are the only ones available (e.g. IPv6 only network).There is a
localIPv6()
accessor, however it appears to have been misinterpretted as being for link-local addresses only, an apparent confusion between local and link-local. This is inconsistent with the IPv4 version which is usually a private or global address (but could be link local).Further the confusing accessor
globalIPv6()
has been introduced.To show the best/most appropriate local address client code needs to check
globalIPv6()
first, thenlocalIP()
, thenlocalIPv6()
, when all they want to know is what is their IP address.Only allowing a single return result is probably okay for IPv4, where multiple addresses are rare, but for IPv6 not only is it common to have at least two addresses (you keep your link-local address when you acquire a global one), but it is also common to have multiple global addresses (although LWIP doesn't yet support DHCPv6 or private extensions, so commonly will only have 2).
I have checked existing issues, online documentation and the Troubleshooting Guide
Historical note - original suggestions:
The text was updated successfully, but these errors were encountered: