It is year 2122, we’ve finally succeed to fight climate changes and some developers have just removed IPv4 support from Linux Kernel. Now when connecting to a WiFi access point, a computer only receives an IPv6 configuration. What are the differences from an IPv4 network ?
Introduction
Today, only a limited part of the Internet has adopted IPv6. If you are interested to get the details, Google makes live statistics about it: https://www.google.com/intl/en/ipv6/statistics.html
So in our current world, a host with only IPv6 configuration and no IPv4 access to internet would have some issues reaching a lot of websites.
But still, imagine a world where anything is reachable through IPv6.

Operating systems and most applications manage IPv6 pretty well. With a bit of luck, a regular user won’t notice any difference while surfing on an IPv6 network.
But what about computer science operations ? About system administration ? What does it change ?
This article explores the issues I met while doing my day-to-day business as a computer scientist.
A summary of the results is available at the end of the article.
Table of contents
Testing policies
Some of the following tools may have many implementation (GNU, BSD, Unix, …) Be aware that the GNU version is always used when there is a choice to be made.
The main goal is to determine how to make basic usage either with a Link-Local Address (LLA) or with a Global Unicast Address (GUA). Every time it is possible, an IPv4 example is provided as reference. An example is also showed to force a tool to use IPv6 when a FQDN or a hostname is provided.
A lot of these tools have a wide range of possibilities, the goal here is not to show that, but how to provide IPv6 addresses as input for these tools. And also showing the extra arguments needed to accomplish that.
Special considerations
Before digging further into what works and what doesn’t. There are two important concepts to be at ease with. You may skip this section if those have no more secret for you.
Link Local addresses and zone id
This family of IPv6 addresses are those starting with prefix fe80::/10
.
Without going into details, here are the basic stuffs to know.
First thing a little bit confusing, it only has a link scope. That means no router will forward link-local traffic out of the link it comes from. Packets with LLA destination won’t leave the link where they were emitted. By link, it means an Ethernet area. It only allows to communicate with other hosts present on that link, it is the only purpose of these addresses. In an IPv4 context, this network area is called the broadcast domain.
Because of that link independence, a link-local address only has to be unique on the link where it exists. It means two different interfaces on the same host could have the exact same link-local address assigned.
Now come the important part, because of all of this, when asking an operating system to establish a communication to a link-local destination, it has no idea what outgoing interface to pick. The destination host could be on any of them. And most of the time, even if there is only a single interface configured with IPv6, operating systems plays dumb and won’t send a packet to a link-local address without a zone id.
So, here comes the zone id, it can be the interface name or the interface index. Even if it is way more explicit to use interface name, some people could prefer to use interface index, it can be easily retrieved with the ip
command.
user@computer:~$ ip -6 address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 state UNKNOWN qlen 1000
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
inet6 2001:db8:1::1/64 scope global dynamic mngtmpaddr noprefixroute
valid_lft 1763sec preferred_lft 563sec
inet6 fe80::1/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
inet6 2001:db8:2::1/64 scope global dynamic mngtmpaddr noprefixroute
valid_lft 1763sec preferred_lft 563sec
inet6 fe80::1/64 scope link noprefixroute
valid_lft forever preferred_lft forever
user@computer:~$
The index number is the number printed just before the interface name:
user@computer:~$ ip -6 address
1: lo ...
2: eth0 ...
3: eth1 ...
user@computer:~$
Now that we know that a zone id is needed to use a link-local address, a way to use it is needed. The representation of that combination is standardized and looks like this:<address>%<zone_id>
.
By using data coming out of the ip
command output, here comes some examples to target the host fe80::25
through the interface eth0
:
fe80::25%eth0
fe80::25%
2
Both example are valid and have exactly the same meaning, except the first one uses the interface name and the second uses the interface index.
Uniform Resource Identifier (URI)
An Uniform Resource Identifier is a standardized and really powerful way to represent structured information about a resource. Because an IPv6 contains colons in its representation and the colons is used by URI to separate the host part from the port part, URI like this could be ambiguous:
https://2001:db8::7:80
Is 80 the port number? or the last hextet of the IPv6 address ? Answer: None of that, in this case the address will be interpreted as a host name, not like an IPv6 address.
To represent an IPv6, the solution proposed is to surround it with brackets:
https://[2001:db8::7]:80
In this case, it becomes clear that 80 is the port number, and the IPv6 is correctly interpreted.
Now an issue may occur when representing a Link-Local address inside an URI. The % character used to separate the address from the zone id my need to be encoded. So sometime this works:
https://[fe80::1%eth0]:80
But sometimes it does not. Because the % character is used by percent-encoded octets. So the % character needs to be encoded, and because its hexadecimal ascii number is 25, the result is %25. The following becomes correct:
https://[fe80::1%25eth0]:80
Be aware that Link-Local addresses are badly supported by URI parsers and sometime it is just not possible at all to use Link-Local addresses.
Here is a summary of the possible representations:
Protocol | Command |
IPv4 | https://192.168.1.1:80 |
IPv6 | https://[2001:db8::7]:80 |
IPv6 LLA | https://[fe80::1%eth0]:80 |
IPv6 LLA with % encoded | https://[fe80::1%25eth0]:80 |
Operation tools
OpenSSH
Version
OpenSSH_8.2p1 Ubuntu-4ubuntu0.1, OpenSSL 1.1.1f 31 Mar 2020
Connection to a SSH server
In this case, brackets are NOT needed, and it does not work at all with them.
Protocol | Command |
IPv4 | ssh admin@192.168.1.1 |
IPv6 | ssh admin@2001:db8::1 |
IPv6 LLA | ssh admin@fe80::1%eth0 |
IPv6 hostname | ssh -6 admin@target |
Port redirection
The following example use Local port redirection and argument -L
. But the syntax is exactly the same for Remote port redirection using argument -R
.
Protocol | Command |
IPv4 | ssh -L 8080:192.168.1.1:80 distant_host |
IPv6 | ssh -L 8080:[ |
IPv6 LLA | ssh -L 8080:[fe80::1%eth0]:80 |
IPv6 hostname | ssh -6 -L 8080:[target_host]:80 (partial support) |
A small explanation about the IPv6 choice over a hostname. SSH allows to pick the version of the Internet protocol used between the current host and the distant host. But there is no way to force the distant host to pick IPv6 or IPv4 to connect the target host.
SCP
Protocol | Command |
IPv4 |
|
IPv6 | scp user@[ |
IPv6 LLA | scp user@[fe80::1%eth0]:/distant/path /local/path |
IPv6 hostname | scp -6 user@target:/distant/path /local/path |
SFTP
Protocol | Command |
IPv4 | sftp user@192.168.1.1 |
IPv6 | sftp user@[2001:db8::1] |
IPv6 LLA | sftp user@[fe80::1%eth0] |
IPv6 hostname | sftp -6 user@target |
Telnet
Version
basic telnet client 0.17
Connection to a host
Protocol | Command |
IPv4 | telnet 192.168.1.1 |
IPv6 | telnet 2001:db8::1 |
IPv6 LLA | telnet fe80::1%eth0 |
IPv6 hostname | telnet -6 target |
Wget
Version
GNU Wget 1.20.3 built on linux-gnu.
Connection to a server
Protocol | Command |
IPv4 | wget http://192.168.1.1 |
IPv6 | wget http://[2001:db8::1] |
IPv6 LLA | not supported |
IPv6 hostname | wget -6 http://target |
Apparently this lack of support for link-local address family is a known bug since a while. It should be resolved in future releases of wget.
Curl
Version
curl 7.68.0 (x86_64-pc-linux-gnu)
Connecting to a server
Protocol | Command |
IPv4 |
|
IPv6 | curl http://[2001:db8::1] |
IPv6 LLA | curl http://[fe80::1%enp0s31f6] |
IPv6 hostname | curl -6 http://target |
Configuration tools
IP
This command allow to do a lot of stuff, from listing stats about interfaces to add routes inside routing table. It is not possible to list every possible actions. For most of them, it is not needed to specify to work with IPv4 or IPv6, the tool infers it depending the action.
Version
ip utility, iproute2-ss200127
Listing addresses assigned to each interface
Protocol | Command |
IPv4+IPv6 |
|
IPv4 |
|
IPv6 |
|
The keyword address
is a sub command of ip
, it does not need to be replaced with a IPv4 ou IPv6 address.
UFW
Version
ufw 0.36
Allowing port tcp 456
Protocol | Command |
IPv4 + IPv6 (simplified) | ufw allow 456/tcp |
IPv4 + IPv6 (advanced) | ufw allow proto tcp from any to any port 456 |
IPv4 only | ufw allow proto tcp from 0.0.0.0/0 to any port 456 |
IPv6 only | ufw allow proto tcp from ::/0 to any port 456 |
Diagnostic tools
Ping
Version
ping from iputils s20190709
Unicast
Protocol | Command |
IPv4 | ping 192.168.1.1 |
IPv6 | ping 2001:db8::1 |
IPv6 LLA | ping fe80::1%eth0 |
IPv6 hostname | ping -6 target |
Multicast
Protocol | Command |
IPv4 | ping -I 192.168.1. 2 224.0.0.1 |
IPv6 | ping -I 2001:db8::1 ff02::1%eth0 |
(with 2001:db8::1 , the address assigned on the outgoing interface eth0) | |
IPv6 LLA | ping ff02::1%eth0 |
Nmap
While nmap was really useful to discover hosts inside an IPv4 network, it is less interesting with IPv6 network. Indeed, an IPv6 subnet has way too many hosts to ping them all. (around 18 * 10^18 addresses to scan for a /64 subnet) To find IPv6 hosts, the simplest way is to send echo request to a multicast destination address called allnodes: ff02::1
. It is possible to do it either with the ping
command or with nmap nse script.
But even without host discovery, nmap stays a powerful tool to make other scans like the port scan or the version scan.
Version
Nmap 7.80 ( https://nmap.org ) at 2020-11-03 10:38 CET
TCP Syn port scan
Protocol | Command |
IPv4 | nmap -sS 192.168.1.1 |
IPv6 | nmap -6 -sS 2001:db8::1 |
IPv6 LLA | nmap -6 -sS fe80::1%eth0 |
IPv6 hostname | nmap -6 target |
Dig
There are two IP settings with Dig. The first setting specifies to talk to a DNS server through IPv4 or IPv6. And the second setting specifies to retrieve an IPv4 record or an IPv6 record.
Version
DiG 9.16.1-Ubuntu
Retrieving an IPv4 record (type A)
Protocol | Command |
IPv4 | dig @192.168.1.1 google.com A |
IPv6 | dig @2001:db8::1 |
IPv6 LLA | dig @fe80::1%eth0 |
IPv6 hostname | dig -6 @dns.google google.com A |
Retrieving an IPv6 record (type AAAA)
Protocol | Command |
IPv4 | dig @192.168.1.1 google.com AAAA |
IPv6 | dig @2001:db8::1 |
IPv6 LLA | dig @fe80::1%eth0 |
IPv6 hostname | dig -6 @dns.google google.com AAAA |
Netstat
Version
net-tools 2.10-alpha
Listing every TCP and UDP sockets
user@computer:~$ netstat -tunaW6
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:631 :::* LISTEN
tcp6 0 0 2001:db8::1:35160 2001:db8::2:443 ESTABLISHED
tcp6 0 0 2001:db8::1:56840 2001:db8::2:443 ESTABLISHED
udp6 0 0 :::45936 :::*
udp6 0 0 :::5353 :::*
user@computer:~$
Two more arguments than usual need to be added. The argument -6
to specify to only output IPv6 information. And the argument -W
to specify wide ouput, without that one, IPv6 addresses are truncated.
SS
Version
ss utility, iproute2-ss200127
Listing every TCP and UDP sockets
user@computer:~$ ss -tuna6
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp UNCONN 0 0 [::]:45936 [::]:*
udp UNCONN 0 0 [::]:5353 [::]:*
tcp LISTEN 0 128 [::]:22 [::]:*
tcp LISTEN 0 5 [::1]:631 [::]:*
tcp ESTAB 0 0 [2001:db8::1]:35160 [2001:db8::2]:443
tcp ESTAB 0 0 [2001:db8::1]:56840 [2001:db8::2]:443
user@computer:~$
Only one more argument is needed with ss
to only show IPv6: -6
TCPDump
Version
tcpdump version 4.9.3
Listening port 443
The IPv6 keyword for TCPDump is ip6
. If no network protocol is specified, TCPDump captures both IPv4 and IPv6 packets. Here are example about capturing packet at destination to https servers:
Protocol | Command |
Both IPv4 and IPv6 | tcpdump -n -i eth0 "tcp dst port 443" |
IPv4 only | tcpdump -n -i eth0 "ip and tcp dst port 443" |
IPv6 only | tcpdump -n -i eth0 "ip6 and tcp dst port 443" |
IPerf
Version
iperf version 2.0.13 (21 Jan 2019) pthreads
Server side
Protocol | Command |
IPv4 | iperf -s |
IPv4 + IPv6 | iperf -s -V |
Client side
Protocol | Command |
IPv4 |
|
IPv6 | iperf -V -c |
IPv6 LLA | iperf -V -c fe80::1%eth0 |
IPv6 hostname | iperf -V -c target |
IPerf3
Version
iperf 3.7 (cJSON 1.5.2)
Server side
Protocol | Command |
IPv4 | iperf3 -s -4 |
IPv6 | iperf3 -s -6 |
IPv4 + IPv6 | iperf3 -s |
Client side
Protocol | Command |
IPv4 | iperf3 -c 192.168.1.10 |
IPv6 | iperf3 -c 2001:db8::1 |
IPv6 LLA | iperf3 -c fe80::1%eth0 |
IPv6 hostname | iperf3 -6 -c target |
Route tracing tools
Route tracing tools only have interest if a flow goes through at least one router. Packets with link-local address as destination should never goes through a single router. So studying this address family with these tool is not really relevant.
But even so, by only using ICMP echo requests, some of these tools are able to work with link-local addresses.
Traceroute
Version
Modern traceroute for Linux, version 2.1.0
Trace
Protocol | Command |
IPv4 | traceroute 203.0.113.1 |
IPv6 | traceroute |
IPv6 LLA | traceroute -I fe80::1%eth0 |
IPv6 hostname | traceroute -6 target |
Argument -I allows to use ICMP echo request in place of ICMP destination unreachable.
Traceroute6
Version
traceroute6: TCP & UDP IPv6 traceroute tool 1.0.4 ($Rev$)
Trace
Protocol | Command |
IPv4 | not supported |
IPv6 | traceroute6 2001:db8::1 |
IPv6 LLA | traceroute6 -I fe80::1%eth0 |
IPv6 hostname | traceroute6 target |
Argument -I allows to use ICMP echo request in place of ICMP destination unreachable.
My TraceRoute (MTR)
Version
mtr 0.93
Trace
Protocol | Command |
IPv4 |
|
IPv6 |
|
IPv6 LLA | not supported |
IPv6 hostname | mtr -6 target |
Paris traceroute
Version
version 1.0
Trace
Protocol | Command |
IPv4 | paris-traceroute --max-undiscovered=30 |
IPv6 | paris-traceroute --max-undiscovered=30 2001:db8::1 |
IPv6 LLA | not supported |
IPv6 hostname | paris-traceroute -6 --max-undiscovered=30 target |
Browsers
Firefox
Version
82.0 (64-bit)
Connexion to a website
Protocol | Command |
IPv4 | https://192.168.1.1:1234 |
IPv6 | https://[2001:db8::1]:1234 |
IPv6 LLA | not supported |
IPv6 hostname | not supported |
Chromium
Version
83.0.4103.116
Connexion to a website
Protocol | Command |
IPv4 | https://192.168.1.1:1234 |
IPv6 | https://[2001:db8::1]:1234 |
IPv6 LLA | not supported |
IPv6 hostname | not supported |
Safari
Version
Version 14.0 (15610.1.28.1.9, 15610)
Connexion to a website
Protocol | Command |
IPv4 | https://192.168.1.1:1234 |
IPv6 | https://[2001:db8::1]:1234 |
IPv6 LLA | not supported |
IPv6 hostname | not supported |
Tools not tested
NMCli
The purpose of this tool is to configure anything related to networking on Linux, it was not possible to find relevant example. But be sure it manages IPv6 as well as IPv4.
Speedtest-cli
This tool does not support IPv6 and does not seem to be maintained anymore.
Dublin traceroute
This tool does not support IPv6.
How to manage Link-Local addresses not supported inside URI ?
A short story that happened to me the other day:
A faulty home router decided to only assigned Link-Local addresses on its network interfaces, its IPv4 stack stopped working for unknown reason. The only way to access its configuration was through a web page. How to proceed with no browser managing Link-Local addresses in their URI parser ?
Solution: SSH port forwarding
This solution consists of creating a ssh local port redirection similar to this:
ssh -N -L '8080:[fe80::25%eth0]:80' localhost
With fe80::25
being the link local address of the target reachable through the network interface eth0
.
This will open the port 8080 on the localhost, and any connexion attempt on that port will be forwarded to the port 80 on the target.
Now the target is reachable through the loopback address on the port 8080. It just needs to open the following URL inside any browser:
http://[::1]:8080
Be aware that this solution have limitation. The address ::1 is sent to the target in the HTTP Header called host
. Some HTTP server won’t allow to receive that value ans will reset the connection. Still, it is possible to trick the HTTP server by changing the header on the fly. Some extension like this one allow to do this.
Summary
Tools | LLA support | Does it need Brackets ? | Argument to Enable IPv6 support | Argument to force hostname resolution to IPv6 |
ssh | yes | no | -6 | |
ssh port redirection | yes | yes | -6 | |
scp | yes | yes | -6 | |
sftp | yes | yes | -6 | |
telnet | yes | no | -6 | |
wget | no | yes | -6 | |
curl | yes | yes | -6 | |
ip | -6 | |||
ufw | ||||
ping | yes | no | -6 | |
nmap | yes | no | -6 | -6 |
dig | yes | no | -6 | |
netstat | -W -6 | |||
ss | -6 | |||
tcpdump | ip6 | |||
iperf | yes | no | -V | -V |
iperf3 | yes | no | -6 | -6 |
traceroute | yes | no | -6 | |
traceroute6 | yes | no | ||
mtr | no | no | -6 | |
Paris Traceroute | no | no | -6 | |
Fiirefox | no | yes | ||
Chromium | no | yes | ||
Safari | no | yes |
Conclusion
To conclude, here is a list of things to keep in mind while using IPv6 with that kind of tool:
- try IPv6 addresses with or without brackets
- while using link-local address, a scope id must always be provided
- the separation character between a link-local address and a scope id is almost always %, I have never observed %25 being used, but keep it in mind, it could appear in some places.
- An extra argument is almost always needed to force a tool to use the IPv6 address when resolving a FQDN or a hostname. Most of the time the argument to use is
-6
. - Link-Local addresses may not be supported by some applications
- If a service is only reachable through its link-local address and over HTTP, try SSH port forwarding to access it
Even if link-local addresses may be really useful, the biggest limitations occurred while using them. We could just hope improvement will come over time.
Sources
- https://tools.ietf.org/html/rfc6874
- https://tools.ietf.org/html/rfc3986
- https://tools.ietf.org/html/rfc3849
- https://tools.ietf.org/html/rfc4007
- https://tools.ietf.org/html/rfc5737
- https://www.bortzmeyer.org/6874.html
- https://bugzilla.mozilla.org/show_bug.cgi?id=700999
- https://stackoverflow.com/questions/45299648/how-to-access-devices-with-ipv6-link-local-address-from-browserlike-ie-firefox
- https://superuser.com/questions/367780/how-to-connect-to-a-website-that-has-only-ipv6-addresses-without-a-domain-name
- https://askubuntu.com/questions/834807/format-of-ipv6-addresses-in-the-netstat-output
- https://bugs.launchpad.net/ubuntu/+source/wget/+bug/1566930
- https://svn.nmap.org/nmap/scripts/targets-ipv6-multicast-echo.nse
- https://addons.mozilla.org/en-US/firefox/addon/modify-header-value/
- https://www.google.com/intl/en/ipv6/statistics.html
Nice article, nice blog, great! Thanks for sharing!