A note on hexadecimal numbers

An analysis of the referrers shows that a lot of people visiting this page don't understand hexadecimal numbers;
An IPv4 address is NOT four decimal numbers, it's one 32 bit number! A string like '' is not an IPv4 addres. It merely represents the actual IPv4 address '01111111000000000000000000000001'. Other representations of the same address are '2130706433' and '0x7f000001'.
An IPv6 address is NOT a collection of digits and letters, it's a 128 bit number! A string like '::1' is not an IPv6 address. It merely represents the actual IPv6 address.
The whole thing is a bit like René Magritte's painting 'Ceci n'est pas une pipe' (This is not a pipe); The painting is not a pipe, it represents a pipe.

Binary to Hexadecimal table

So that's 2 hex digits to represent 8 bits (a byte), 8 for 32 bits (4 bytes) and 32 for 128 bits (16 bytes).
A group of 4 bits is also known as a nibble (little byte). A group of 4 nibbles (16 bits or 2 bytes) is sometimes referred to as a 'quad nibble'. EG;
0db8 = 0000 1101 1011 1000
It takes 8 quad nibbles to represent 128 bits.

Hexadecimal is better then decimal

Hexadecimal numbers are in fact easier to use then decimal numbers. This is because in quad decimal notation (EG each number represents 1 out of 256 possible 8 bit patterns. Whereas in hexadecimal notation, each hex digit represents just 4 bits. This makes things a lot simpler.

For instance, try to find out the binary value for decimal '168'. Wat you need to do is find which powers of two are in there;

02⁰0000 0001 1
10000 0010 2
20000 0100 4
30000 1000 8
42⁴0001 0000 16
52⁵0010 0000 32
62⁶0100 0000 64
72⁷1000 0000128

168 ≥ 2⁷ (128), so the biggest number that fits in there is 2⁷. This means that the most significant bit, bit 7 is set;

 Decimal        Binary

 - 128          1??? ????

40 < 2⁶ (64), so bit 6 is zero;

                10?? ????

40 ≥ 2⁵ (32), so bit 5 is one;

 -  32          101? ????

8 < 2⁴ (16), so bit 4 is zero;

                1010 ????

8 ≥ 2³ (8), so bit 3 is one;

 -   8          1010 1???

There is nothing left so all the other bits are zero;

                1010 1000

Now do the same thing for hex 'a8'. All you need to do is look it up in the table above;

 a  = 1010
  8 =      1000
 a8 = 1010 1000

If you want to do this without a table, values 0 to 7 are pretty easy to do by hart;

HexPowers of 2Binary
00 + 0 + 0000
10 + 0 + 1001
20 + 2 + 0010
30 + 2 + 1011
44 + 0 + 0100
54 + 0 + 1101
64 + 2 + 0110
74 + 2 + 1111

For the rest, think of hex values 8 to f as sums of 8 plus 0 to 7;

HexDecSumPowers of 2Binary
8 88 + 08 + 0 + 0 + 01000
9 98 + 18 + 0 + 0 + 11001
a108 + 28 + 0 + 2 + 01010
b118 + 38 + 0 + 2 + 11011
c128 + 48 + 4 + 0 + 01100
d138 + 58 + 4 + 0 + 11101
e148 + 68 + 4 + 2 + 01110
f158 + 78 + 4 + 2 + 11111

When dealing with host addresses, network addresses and netmasks, doing things hex is a lot easier.

A note on netmasks

An analysis of the referrers shows that some of you are looking for stuff like netmasks, CIDR and slash-notation. For instance, what is the netmask of all addresses from 2001::0 to 2002::0 ?
More about this stuff on this page.

If you want to be a network administrator, you have to understand binary (base 2) and hexadecimal (base 16) systems.
Furthermore you need a thorough understanding of logic operators like AND, OR, NOT and XOR.

A note on RFC 1918 and other reserved IPv4 addresses

An analysis of the referrers also shows that a lot of people are looking for the IPv6 equivalent of RFC 1918 and other reserved IPv4 addresses;

IPv4IPv6 equivalent

See address types or IPv6 address classes for more information.


One thing which initially confused me was the way IP addresses are often phrased. EG;


This is in fact several statements rolled into one;

Host address:    2001:db8:1234:abcd:5678:ef90::1
Network address: 2001:db8:1234:abcd::0
Netmask:         /64

From RFC 2374;

3.1 Aggregatable Global Unicast Address Structure

   The aggregatable global unicast address format is as follows:

     | 3|  13 | 8 |   24   |   16   |          64 bits               |
     |FP| TLA |RES|  NLA   |  SLA   |         Interface ID           |
     |  | ID  |   |  ID    |  ID    |                                |

     <--Public Topology--->   Site
                                     <------Interface Identifier----->


      FP           Format Prefix (001)
      TLA ID       Top-Level Aggregation Identifier
      RES          Reserved for future use
      NLA ID       Next-Level Aggregation Identifier
      SLA ID       Site-Level Aggregation Identifier
      INTERFACE ID Interface Identifier

The top (most left) 48 bits are set by your provider. The next 16 bits are basically network addresses. That's 2¹⁶=65536 networks! On each network there are max 2⁶⁴ interface addresses!
A /etc/network/interfaces (Debian Linux) example;

iface eth0 inet6 static
        pre-up modprobe ipv6
        address 2001:db8:1234:1::6
        netmask 64
        gateway 2001:db8:1234:1::1

Replace '2001:db8:1234' with your own /48.

Dual stack RFC1918 - IPv6 example

In my home town 172.16.x.y to 172.31.x.y addresses are used by a large wifi network. 10.x.y.z addresses are used by the link between my modem and my server, (which also acts as a gateway and NAT box). This leaves 192.168.x.y addresses for use on my LAN.
On my LAN I use a subdomain 'int', EG: 'int.example.com'. I simply mapped 'int' to 'ip6' and 192.168.x.y to 2001:db8:1234:x::y. This way IPv6 nameserver zone files can be easily derived (with a shell script) from IPv4 private net zone files. So a host with IPv4 address '' also has IPv6 address '2001:db8:1234:1::6';

Host namepc6.int.example.compc6.ip6.example.com
IP address192.168.1.62001:db8:1234:1::6

This reduces the number of address from 2⁸⁰ to 2¹⁶ [1], but that's still a lot.

There is also a third hostname which points to both IPv4 and IPv6 addresses (the RFC1918 addresses are of course invisible to the outside world). So a host 'pc6.example.com' might have both '' and '2001:db8:1234:1::6' as its address.
Below an example;

pc6:~$ ping(4) www.example.com
PING www.example.com ( 56(84) bytes of data.
64 bytes from www.int.example.com ( icmp_seq=1 ttl=64 time=0.218 ms
64 bytes from www.int.example.com ( icmp_seq=2 ttl=64 time=0.222 ms
64 bytes from www.int.example.com ( icmp_seq=3 ttl=64 time=0.216 ms

pc6:~$ ping6 www.example.com
PING www(www.ip6.example.com) 56 data bytes
64 bytes from www.ip6.example.com: icmp_seq=1 ttl=64 time=0.222 ms
64 bytes from www.ip6.example.com: icmp_seq=2 ttl=64 time=0.220 ms
64 bytes from www.ip6.example.com: icmp_seq=3 ttl=64 time=0.219 ms

[1] Actually 2¹⁶ - 512; In 192.168.x.y, 'y' cannot be 0 or 255. So the IPv4 addresses are to The IPv6 addresses are therefore 2001:db8:1234:0::1 to 2001:db8:1234:ff::fe (assuming 256 networks of max 254 hosts each).

Larger ranges

The similar trick can be used for larger address spaces;

For instance, a /8 rfc1918 address;

Convert from decimal to hexadecimal;


Combine bytes into quad nibbles;

0a14  1e28

Insert colon;


Prepend prefix;


Some more examples;

IPv4IPv6 2001:db8:1234::0a00:0001 2001:db8:1234::0aff:fffe 2001:db8:1234::ac10:0001 2001:db8:1234::ac1f:fffe 2001:db8:1234::c0a8:0001 2001:db8:1234::c0a8:fffe

You may omit the leading zeros from each quad nibble;

IPv4IPv6 2001:db8:1234::a00:1 2001:db8:1234::aff:fffe 2001:db8:1234::ac10:1 2001:db8:1234::ac1f:fffe 2001:db8:1234::c0a8:1 2001:db8:1234::c0a8:fffe


Below a converter.
Submitting two numbers separated by a single dot (ddd.ddd) will use the first method (xx::xx), four numbers with three dots (ddd.ddd.ddd.ddd) the large range method (xxxx:xxxx);

WARNING! The stuff above is not to be confused with the functional equivalent of rfc1918 or other reserved IPv4 addresses.
See address types or IPv6 address classes for more information.

IPv6 ↔ IPv4 conversion proxies

I run Bind with views; I have different zone files for the outside world and my LAN. In the internal zone file are host names like imap, ns, ntp, proxy and smtp. These hostnames are invisible to the outside world and have both a (RFC1918) IPv4 and a IPv6 address. Clients on the LAN can choose to connect to either the IPv4 or the IPv6 address. Likewise daemons like the mailserver, nameserver, ntpd or web proxy can connect to both IPv4 and IPv6 addresses. This way IPv4 only clients can connect to IPv6 only servers and vice versa.
As web proxy Squid 3.5 is used.


For IPv6 NTPD wants hex netmasks. EG;

restrict mask nomodify
restrict -6 2001:db8:1234:abcd:: mask ffff:ffff:ffff:ffff:: nomodify


restrict mask nomodify
restrict 2001:db8:1234:abcd:: mask ffff:ffff:ffff:ffff:: nomodify

IPv4 - IPv6 PPPD differences

  1. The PPPD may use IPv6 link-local addresses (fe80::/10) instead of IPv6 global-unique addresses for the ends of the PPP link.
  2. These addresses may be different each time the PPP link comes up.
  3. The PPPD may not set an IPv6 route to the remote end of the link.
  4. The PPPD may not set an IPv6 default route to the remote end of the link, even if the PPP config file says it should.

To compensate for the 3rd: In a script run when the PPP link comes up, put:

# Set IPv6 route to remote end
if ! ( ip -6 route | grep -q "${PPP_REMOTE}" )
	ip -6 route add "${PPP_REMOTE}" dev "${PPP_IFACE}"

To compensate for the 4th: In a script run when the PPP link comes up, put:

# Set IPv6 default route to remote end
if ! ( ip -6 route | grep -q default )
	ip -6 route add default dev "${PPP_IFACE}"

A bit of programming

Suppose you want to write a little daemon. Something that listens on all interfaces. And no ACLs. No options to make the server IPv4 only or IPv6 only.

The easiest way to implement this is to use a combined IPv4 - IPv6 socket (not all OS-es support this). Creating an IPv6 only listening socket will usually do the trick. If not, use setsockopt() to set 'IPV6_V6ONLY' to '0'. Do this after you set 'addr6.sin6_family' to 'AF_INET6'. A bind to '::' will now also bind to ''.

If you insist on separate sockets, set 'IPV6_V6ONLY' to '1'.

IPv6 link-local addresses and browsers

AFAIK there are no cross platform FOSS GUI web-browsers that support IPv6 link-local addresses. Or rather, they don't support zone-indices. And you need zone-indices to use IPv6 link-local addresses.
Lynx is a FOSS cross platform web-browser, but it is a text browser and doesn't support tables.
Still, if you need set set the IP address of an embedded device and you can only connect to it using an IPv6 link-local address, Lynx is probably the way to go.


Not found what you were looking for? Have a look at this page.

IPv6 Ready