IPv6 Things

(Redirected from IPv6 Things,)

IPv6 Things start page.

IPv6 Things is a web server that presents a variety of "things" with their own dedicated IPv6 address. Inspired from prior concepts like Aliases 3000 and the ip route add local trick, and from observing the behavior of web servers with respect to the network and application layers, the IPv6 Things server effectively mimics a series of more than 41,000 dedicated servers, each with their own dedicated IPv6 address.

The source code of IPv6 Things has moved to https://git2.peterjin.org/ipv6-things.

Detailed introduction

IPv6 Things is a simple proof-of-concept that illustrates how it is possible to give a variety of things their own IPv6 address. Originally extending from the IPv6 Bible and also a small part of my custom IPAM software, the IPv6 Things project attempts to give as many things as possible their own IPv6 address in a way that simply cannot be done in IPv4.

There are two main parts to IPv6 Things:

  • The IP address management software, which assigns a name (symbolic of an object) to each of the IP addresses and generates forward and reverse DNS records.
  • The server-side component, which reads the server's IP address to deliver an individualized webpage.

The web app was written using Node.js and Express. Internally, all these websites are handled by one server with a static /64 route and the so-called "AnyIP" trick. The server will respond on any IP address within that /64, and we can read that IP address (hereinafter the "server IP address") by calling getsockname() on a connection socket returned by accept() (nginx exposes this via the "$server_addr" variable, and Node.js exposes this via the "localAddress" property of a socket on the listener callback). The server can change its behavior depending on what IP address it was referred to; specifically, there is a function that takes in the server's IP address and displays the "content" associated with that IP address. For example, if the server IP address is 2602:806:a003:40e::3000:3131, then the contents of "william.aliases.peterjin.org" is shown. If the server IP address is 2602:806:a003:40e::5c50:49, then "SCP-049" is shown. Tradition tells us that different IP addresses correspond to different servers, so from the perspective of anyone else on the Internet, each one of those websites is its own little server, with the "You are connected to (domain/IP)" page shown as the "default" site.

Technical explanation of how it works

Warning: long and may not be entirely relevant to the layperson. Click on "expand" on the right to view contents.

Strace of the Node.js main thread while it serves a connection. Immediately after calling accept(), Node.js calls getsockname() on the new socket descriptor. The resulting address is used to make decisions like selecting the correct SSL certificate (sent on the first reply, just after the first set of getpid() calls). This preliminary operation is all performed before the TLS handshake happens, so no SNI is required. 2602:806:a003:40e::3000:2451 corresponds to daniel.aliases.peterjin.org and is an element of 2602:806:a003:40e::3000:0/100, so it responds with the "main" certificate (or the "aliases" certificate, if I were to have a separate certificate for that group) rather than e.g. the "ipv6bible" certificate.

The following lines of code are central to the operation of IPv6 Things: [1] and [2]. The findServerForIP function takes in the "server IP address" as input, and returns the HTTP and HTTPS server objects that can handle that connection. None of those HTTP and HTTPS server objects listen on TCP ports directly; instead, only the "master" server (the net.createServer one in index.js) listens on TCP port 443, and the only way of using those other server objects is to emit the "connection" event manually on those objects from the "master" server, with our new socket as its sole argument. This is similar to how socketbox could be implemented, but with Node instead of C/Python.

Whenever the server receives an incoming TCP connection, findServerForIP is called, returning an HTTP or HTTPS server object that can accept that connection. (If there is no such server, then the connection can be dropped; however, in IPv6 Things, that is not possible and it can be ignored.) On that server object, we can emit the "connection" event with a socket as argument; by doing so, the server acts as if that socket was, in fact, its own incoming connection.

Creating 40,000 server objects and SSL contexts, however, would take up too much memory. Instead, we only create a handful of server objects, one per SSL certificate (there are six for the IPv6 Bible, one for ipv6-things.com, and one for the rest; previously, there were in fact multiple SSL certificates for each one of the groups below, so previously five or six certificates for the rest; however, nowadays they're all combined into one.). Each server is defined by two properties: 1) the SSL certificate or TLS context associated with the server, and 2) the Express.js application object that is used to handle requests made to that server. From those properties, we can construct the HTTP and HTTPS server pair that is used to handle both HTTP and HTTPS connections to various groups of IPv6 addresses, and those server objects will be what the above procedure will operate on. Note that each server object described above was meant to handle a "partition" of the address space (smaller than the entire space (/64) but significantly more than one address (ideally a /96 or /112)), rather than just a single address.

As a result of the above, there are actually only about 10 server objects in total (2 HTTP server objects and 8 HTTPS server objects). To actually give off the impression of 40,000 servers, however, we do something similar to what was already done above with the SSL certificate again, but as a per-request lookup that happens after the TLS handshake; see [3] and [4]. This function takes in the "server IP address" as before, but it looks up the "domain" (text in the green box), "caption" (text in the blue box), and "auxtext" (text below the "random" links) instead (all of those are short strings, so the memory overhead is minimal, and in the case of SCP numbers, is actually automatically generated). Using this information, the resulting web page is generated on the fly and sent back to the client, with the strings shown in the green, blue, and description boxes. Since any domain name pointing to the object will result in the same outcome, it effectively gives off the impression that the IP address is dedicated to that object.

The HTTP server works slightly differently compared to the HTTPS server. While the HTTPS server is capable of operating in the manner described above, the HTTP server is not. Instead, the HTTP server is just a single Express server with middleware routing (i.e. on every connection, the correct middleware object is located based on req.socket.localAddress, and req, res, and next are passed through to the middleware function, but is otherwise conceptually identical to the procedure above with HTTPS). The reason for this is because we want consistent redirect behavior across all objects, even if that ability is not present in the HTTP server defined for a given object. Specifically, HTTP->HTTPS redirects should happen if and only if the domain in the Host header matches a certain list (TODO: allow customization of that list), so that users accessing it from different domain names won't instantly see a SSL warning; and /.well-known/acme-challenge should always redirect to a single directory to make it easier to obtain SSL certificates.

The SSL certificates used in the live demo are multi-domain wildcard certificates from Let's Encrypt, so the certificates are all free :). Let's Encrypt requires DNS verification for wildcard certificates, so the /.well-known/acme-challenge path defined in the port 80 server is only good for single domain names. The main purpose of that path is to support corner cases that the wildcard certificates don't cover (such as this one), though I have also used this method to obtain a few useless certificates just for s*** and giggles (like this one). For DNS verification, however, to reduce the burden of having to update IPAM scripts every time this verification is performed, we use a CNAME record at the _acme-challenge.<domain> location to redirect the verification to a more easily updatable DNS server (we will publish the internals of that later, but as of right now it's just a single BIND DNS server whose entries need to be manually updated and signed with DNSSEC).

The /.well-known/acme-challenge path points to an express.static instance in a fixed directory, so you can simply use Certbot's "webroot" authenticator to obtain HTTP-verified certificates where applicable. This directory is shared between all objects on all IP addresses that the server listens on, so you can use that directory as the webroot for all HTTP challenge verifications of a multi-domain certificate.

Finally, because we only use the IP address to determine the destination server, we have to take the following security consideration into account, especially since we're using wildcard certificates: In terms of the TCP/IP network model, our setup is as if we had 40,000 separate hosts on a single network (even though that is not physically true), each running their own HTTP and HTTPS web server. However, the setup is also as if all the HTTPS web servers shared the same wildcard SSL certificate. This would mean that the connection to one host could be intercepted and redirected to another; for example, via DNS cache poisoning, ARP spoofing, packet header modification, etc. Normally, in a more sane SSL deployment, the SSL certificate for the other host would not match that of the original host, and the connection would be therefore rejected. (In the case of a multi-domain SSL certificate, the server would just use the Host header to differentiate servers; this works because the Host header is encrypted, so it's more reliable than checking the IP address). However, in our situation, since all the hosts have the same SSL certificate, this attack scenario would not be detected. To guard against this, the IPv6 Things server has a mechanism that would reject SSL connections if the Host header matches a "secure domain pattern" (a regex that encompasses all domain names that the server uses, or more specifically, all domain names that the server has an SSL certificate for) and the Host header does not match the "domain" property of the object found from the server IP address. This effect is similar to as if each host in the scenario described above only accepted SSL connections if the Host header matched its expected domain; there are four cases to consider in this regard, as a proof of correctness:

  1. The Host header domain name falls outside of the secure domain pattern (e.g. http://2602-0806-a003-040e-0000-0000-0000-0001.has-aaaa.name). No HTTP->HTTPS redirection is performed as we do not have an SSL certificate for that domain name, and HTTPS connections would already be rejected for having a mismatch in the certificate's domain name.
  2. The Host header domain name falls within the secure domain pattern, but no SSL certificate has that domain name listed (e.g. https://i6t-cf-poc-001.peterjin.org). This is essentially the same as the first case, except that the HTTP->HTTPS redirection will always occur, which means that the user will only see an SSL error.
  3. The Host header domain name falls within the secure domain pattern, an SSL certificate does exist for that domain, but that domain does not match what is expected for that object (e.g. https://xn--fjaaa5308aabaaaabab.scp.rdns.peterjin.org). In that case, no SSL error would occur, but we can also check the Host header to see if the domain there is "scp-2521.scp.rdns.peterjin.org", and reject the connection if it does not match. By doing so, the availability space for a given IP address has been reduced from the entire set of domain names in the SSL certificate to just that one domain.
  4. The Host header domain name falls within the secure domain pattern, an SSL certificate exists for that domain, and the domain matches what is expected for that object (e.g. everything listed in List of IPv6 Things subdomains). In that case, we can just display everything as usual.

We do not need to perform the check described in the third and fourth cases for domains outside the secure domain pattern since the mismatched SSL certificate would have detected it anyway.

This check is present in the IPv6 Bible, as well as the static sites behind some of the "things" (e.g. [5]), but is currently not present when displaying the IPv6 Things purple background page as there are some parts (e.g. the i6t-cf-poc links, as well as the SCP CNAME records) where the mismatch is expected.

Live demos

World IPv6 launch logo.svg No-ipv4.svg The services described here are IPv6-only, without any IPv4 addressing. If you have trouble viewing these pages, then you do not have IPv6 connectivity. Unless you know you should have IPv6 connectivity, this is usually not due to an issue with the firewall or web filter (if present in your network). If you have a cell phone, you may need to turn off your Wi-Fi or VPN and use cellular data instead.
Excellent! Your ISP appears to support IPv6! You shouldn't have any problem viewing these webpages.
Checking your internet connection for IPv6 support. If this text does not change, then it looks like your ISP does not support IPv6. Please disable or enable any proxies or VPNs, switch to mobile data, or simply just find another ISP.

Came here from ungleich.ch? Start here: https://ipv6-things.srv.peterjin.org

For a complete list of subdomains, see List of IPv6 Things subdomains.

The canonical version of IPv6 Things uses the 2602:806:a003:40e::/64 subnet, as well as the 2602:806:a003:40f::/64 subnet to provide "things" for the Traceroute Text Generator.

New since 31 October 2020: You can now query the IP addresses on our WHOIS referral server, rwhois.peterjin.org, port 4321. This referral server URL is also provided to ARIN, so looking up the IP addresses using the 'whois' command will also reveal information about these IP addresses.

CIDR range[1] Usage Number of objects/IP addresses used Example Notes
2602:806:a003:40e::1 introduction page 1 https://ipv6-things.srv.peterjin.org Used primarily only as a start page, to introduce this website to anyone who looks at this from an external source.
2602:806:a003:40e::1:0/112 ipv6-things.com subdomains about 20 http://ipv6-things.com (2602:806:a003:40e::1:1) Subdomains are usually common subdomains for websites, and each one has its own IPv6 address.
2602:806:a003:40e::1869:0/112 Elements of the periodic table 118 https://nitrogen.ptable.misc.peterjin.org (2602:806:a003:40e::1869:7) The periodic table was invented in 1869. Last part is the atomic number of the element.
2602:806:a003:40e::3000:0/100 Aliases 3000 about 250 https://sebastian.aliases.peterjin.org (2602:806:a003:40e::3000:2f81) Unlike other name lists used in Aliases 3000, the names listed here is an exhaustive list. This means that even names that I would never use to refer to myself are listed here, in order to fulfill the overall goal of assigning as many things as possible their own IPv6 address. The presence or absence of a name does not necessarily imply that I will necessarily be referred to by that name in the future. In addition, any similarities of hex digits to names is purely coincidental.
2602:806:a003:40e::5c50:0/108[2] SCP Foundation/Wiki objects 10,000 (including possible unassigned SCP numbers) I am a toaster[3] accessible under the IP address 2602:806:a003:40e::5c50:426 (https://scp-426.scp.rdns.peterjin.org) that can only be referred to in the first person. Currently, SCPs only go up to 6,000, but 10,000 DNS records are provisioned to allow for future expansion. We may change this to dynamic record generation, as currently all the records are static, which means that we need to generate 40,000 RRSIGs (AAAA + PTR + NSEC3) every time we make a DNS update.
2602:806:a003:40e:0:2::/96 Miscellaneous random things about 10 https://in-honor-of-george-floyd.misc.peterjin.org (2602:806:a003:40e:0:2:2020:525) George Floyd died on 2020-05-25.
2602:806:a003:40e:0:3::/96 CSS/SVG colors about 150 https://blue.colors.misc.peterjin.org (2602:806:a003:40e:0:3:ff00:ff), https://khaki.colors.misc.peterjin.org (2602:806:a003:40e:0:3:fff0:e68c) Last 24 bits of IP address represent the RGB value of the color.
2602:806:a003:40e:4000::/66 Astronomical objects about 100 not yet operational Inspired because of claims that you can give entire internets to stars.
2602:806:a003:40e:b1be::/80 IPv6 Bible 31,000 https://1-1.genesis.ipv6.bible (2602:806:a003:40e:b1be:7700:1:1) The IPv6 Bible alone consumes more than 120,000 RRSIGs!
2602:806:a003:40f::/64 Traceroute Text Generator about 450 https://scp-pjin-003.t.rdns.peterjin.org/ (2602:806:a003:40f::3:1f) Imported as a side effect of initially using this web server to provide videos for the Traceroute Text Generator and subsequently replacing all dots with hyphens so that browsers no longer complain about the SSL certificate when an intermediate "router"'s URL is typed in. This subnet also has a slightly different routing policy to allow the Traceroute Text Generator to work, so no "normal" objects are allocated from this subnet.

The domain names were meant to be rather long, just to show that because of the abundance of IPv6 addresses, it is possible to create very deep hierarchical structures.


Screenshot of https://ipv6-things.srv.peterjin.org. If no caption is provided, then only the domain name is shown.
http://[2602:806:a003:40e::2]. If an IP address is not associated with a domain name, then only the IP address is shown.
Since there is a reverse DNS entry for all of these IP addresses, they'll show up in traceroutes.

What is this?

Extending from the IPv6 Bible, this project serves as a simple demonstration as to how the AnyIP trick may be useful. The system is designed such that it may appear that the IP addresses all map to separate hosts (with characteristics to strengthen that illusion such as reverse DNS and not requiring SNI), even though everything happens on a single server.

Fun fact: Because the SSL certificate to serve is determined entirely by the server IP address, if you test any of the hostnames here on the Qualys SSL test, then it will not say anything about requiring browser SNI support.

How does it work?

Most of the magic comes from using the $server_addr variable. By subjecting the value of this variable to a number of lookups, we can change the server's behavior just based on the IP address, a variable already supported by DNS lookups.

For example, if we have the following configuration:

geo "$server_addr" $ssl_certificate_selector {
    default main;
    2001:db8::/64 alt1;
    2001:db8:0:1::/64 alt2;

ssl_certificate "/etc/nginx/ssl/$ssl_certificate_selector.crt";
ssl_certificate_key "/etc/nginx/ssl/$ssl_certificate_selector.key";

then any address within 2001:db8::/64 will receive the "alt1" certificate, addresses within 2001:db8:0:1::/64 will receive the "alt2" certificate, and anything beyond that will receive the "main" certificate.

To put this into context, imagine that the "main" certificate had *.item1.example.com as a common name or SAN, and the "alt1" certificate had *.item2.example.com as a common name or SAN. One would put as many items that would fit under item1.example.com, provided that the IP addresses for those DNS records are outside of 2001:db8::/64 or 2001:db8:0:1::/64. Similarly, under item2.example.com, the IP addresses would all be within 2001:db8::/64.

Nginx only does the frontend work of SSL termination, at which point we need to inspect $server_addr because it is needed to select the SSL certificate. But that's only the beginning. Next, we need to find a way to differentiate between these addresses at a smaller scale, so that it actually appears to an outside observer that they are all different servers. We could, for instance, create 42,000 virtual hosts, each with their own domain name and IP address, but unfortunately, I don't think nginx really scales that well[4]. So as an alternative, I used "proxy_pass" to pass all requests onto a Node.js webapp, with $server_addr as an additional header (think X-Forwarded-For but with the server IP rather than the client IP). The Node.js application reads this IP address, and then using a lookup table/JSON file, it displays the domain name that it finds in its database. Only the IP address is used in this lookup procedure; Host header and SNI name are generally ignored. Thus, we are able to give 42,000 things their own IPv6 address, in a way that scales very well with the number of objects.

The mapping of objects/domain names to IP addresses is a bijection or a one-to-one function; every domain name involved is mapped to exactly one IP address (via an AAAA record), and that IP address maps back to that domain name (via a PTR record). Every IP address involved is mapped to exactly one domain name, and that domain name maps back to that IP address. It's not a perfect bijection, since some of the colors map to the same IP address, but otherwise, it's a bijection. This means that "scott.aliases.peterjin.org" is equivalent to "2602:806:a003:40e::3000:2f51", and specifying either one of these two identifiers is enough information to imply the other. Note that this live demo has PTR records on its IP addresses only to demonstrate this possibility; the web app itself does not use PTR records, and we will not put down any self-hosted version of IPv6 Things solely because it does not have PTR records set (i.e. the operator of that self-hosted version does not have access to reverse DNS or has hit a limit as to how many PTR records they may have).

ELI5 (third person): So basically what he did was threefold. First, he set up a web server with a static /64 subnet, and he configured the system such that the server responds on every address within that /64. Second, he set up a bunch of subdomains on his main domain whose addresses are within that /64, using a variety of categories (aliases, SCPs, elements of the periodic table, colors, etc.), such that whenever you go to those domains, it hits the webserver (even though they have different IP addresses). And finally, he configured the web server such that it is able to read which IPv6 address the server was referred to, and the server can provide the correct website or otherwise change its "personality" (SSL certificate, document root, etc.) based on this IPv6 address. Only the server's IP address is checked ("Host" and SNI are ignored), so it gives off the illusion that each one of those "things" has its own dedicated server that is not shared with any other "thing", where the "You are connected to" site appears to be the "default" site for each of those "things".

Where did you get the idea?

As I always say, IP addresses define services, not hosts. By exploiting the fact that IPv6 has a much larger address space and that servers are already identified by IP addresses, we can encode up to 64 bits of discretionary information in the bottom half of the IPv6 address, as sort of specifying additional information to existing services transparently without any client support. In terms of the TCP/IP and OSI models, we have basically moved the parsing of the bottom 64 bits of the IP address from the network layer to the application layer.

Another factor was because of how I intended to operate Aliases 3000. For normal people with only a single, canonical name (akin to a single IP address), their behavior is mostly the same regardless of who talks to them, but with Aliases 3000, I can sort of modify my behavior based on what name they call me, and this makes it appear that I'm more than one person, even though I'm actually only one person. This project is the same, but with connection 4-tuples instead of names.

I also remember reading somewhere about a certain DNS service where if you accessed it via IPv4, you would have to put in your own IP address so that it can identify you, but if you accessed it via IPv6, they would give you a dedicated IPv6 address to use as your nameserver, since they can just use parts of the server IPv6 address to identify the customer.

Finally, websites that have their own dedicated IP address tend to be "large" sites, whereas sites with shared IP addresses and name-based virtual hosting tend to be "small" sites. From this reasoning, each of the IPv6 Things websites is a "large" site, despite having only a single page :).

Does this exist elsewhere?

Even after searching lists of IPv6-only websites, I think this is the only similar thing that exists on the Internet in terms of its network structure.

The 42.be demo also involves giving "squares" their own IPv6 address to form a Nyan Cat mosaic, however, each one of the squares is specified by the path in the URL, not the IP address, which would be contrary to the central idea of IPv6 Things.

Is there a complete list of available subdomains?

See List of IPv6 Things subdomains.

What is the purpose of the retired-x-yyy aliases subdomains?

Occasionally, I might remove names from the "aliases" group because of reasons that I don't disclose. If I were to simply remove the name from the list, then it would effectively shift all the names that come after it one place back. The retired-x-yyy names are used to ensure that such names remain in the same place, even when some of the names are removed from the list.

How do I know that name-based virtual hosting is not used?

Put the following into your /etc/hosts file:

2602:806:a003:40e:b1be:7700:1:3 no-sni.test

If you then browse to no-sni.test, you should see the contents of Genesis 1:3. You can replace the 7700:1:3 part to obtain a different Bible verse.

Similarly, you can telnet 2602:806:a003:40e:b1be:7700:1:3 80, then type GET /, and the HTML code of the Bible verse should appear. Because you did not type a Host header, this server does not use name-based virtual hosting.

The IPv6 Things server is a true IP-based virtual host. In other words, it actually checks the destination IP address of the incoming connection and changes its "personality" based on that IP address; Host header is ignored[5].

Because of this special behavior, all of the following URLs display the same content ("Daniel"), despite the variations in domain names, since they all resolve to the same IP address:

Similarly, all of the following URLs show "sodium":

This is different from:

  • 42.be, which relies on the pathname to retrieve the correct image
  • (2606:4700:4700::1111), which actually seems to rely on "" ("[2606:4700:4700::1111]") in the Host header to retrieve information about the DNS service. In other words, the web server uses name-based virtual hosting, and treats the IP address literal as one of its hostnames. This means that alternative names like http://01.01.0101.ip4.static.sl-reverse.com or http://2606-4700-4700-0000-0000-0000-0000-1111.has-a.name will not work, despite having the same IP addresses.

Wouldn't this overload the neighbor table?

We use static routing without neighbor discovery. In general, static routing is available anywhere where BGP sessions are offered.

Some of the stuff just sounds like total nonsense!

You have to use your imagination, especially with the aliases and SCPs :).

I want to host this myself. Where do I start?

You will need:

  • a /64 static route from your hosting provider, or a BGP session
  • Delegation of the reverse DNS zone corresponding to that /64 (optional). If you use PI space from ARIN/APNIC/RIPE/whatever and BGP sessions, you will do this through the ARIN/APNIC/RIPE portal. Most of us won't have the luxury to have over 300 PTR entries (I use custom nameservers to support this). If you don't have access to reverse DNS, that's OK; it's mostly for vanity purposes and the web app will work fine without it.
  • Delegation of a subdomain (NS records) of a domain name you have DNS access to.

Use the 'ip route add local' trick described below to bind the entire /64 to your server.

Use the example nginx config below as part of the frontend. The Node.js program in https://gitlab.peterjin.org/_/ipv6-things is the backend.

Unfortunately, there are so many parts to this web app that it's impossible to have a clear view of all of the source code involved in the making (i.e. it's not a trivial "node index.js" or even a "docker run". If you would like to host this anyway, contact me for information, and I can provide detailed step-by-step instructions.

See also IPv6 Things/Configuration.

How would this work in a high-availability/anycast configuration?

You would do the same thing as always, but each server will have its own static route.

  • Each server can run the server-side software independently.
  • If normally, the redundant servers are behind the same IP address, then you would route the same /64 to both servers.
  • If normally, the redundant servers are behind different IP addresses, then you would route a different /64 to each server.
  • Essentially, we route the entire /64 instead of single IP addresses.

Wouldn't someone sniffing the link see which website I'm on, despite the use of encryption?

True, seeing the destination IP as 2602:806:a003:40e::5c50:173 strongly suggests that this is a connection to the SCP-173 site rather than, say, the SCP-426 or SCP-294 sites. Encrypted SNI would not help, since the server never even uses plaintext SNI in the first place.

However, securing the server in a way that prevents anyone from seeing which site was visited is beyond the scope of this project. On the other hand, in situations where this is desirable, there are a few solutions:

  • Randomize the IP addresses assigned to each website and omit PTR records.
  • Set up a custom DNS server that returns a random AAAA record within the /64 subnet for a certain domain name, and CNAME each "object" domain to that domain name. This increases the number of connections per client IP address by 2^64-fold, but unless the DNS server is somehow linked to the web server, you would still have to rely on the Host header to determine which site was visited.
  • If the DNS server was actually linked to the web server in the scenario above, then the bottom /64 could serve as a "token" that replaces SNI and the Host header. For example, if example.com was hosted on this server, then a lookup for example.com could return 2001:db8:0:1:9999:1234:5678:aaaa, and then the web server could by dynamically reconfigured such that if it sees the server IP of 2001:db8:0:1:9999:1234:5678:aaaa, then it would serve the certificate for example.com if no SNI header was already present. Later, example.com could return 2001:db8:0:1:1234:4444:2222:1010, and the server would be reconfigured again such that a server IP of 2001:db8:0:1:1234:4444:2222:1010 returns the certificate for example.com.
  • Ultimately, any solution that maintains the spirit of IPv6 Things would inevitably disclose which site was visited, since the IP addresses can always be entered in directly to visit the exact same site. But this was inherent to the structure of IPv6 Things, right?

Permitted things

  • Any set of objects, physical or virtual, may be used.
  • Preference may be given to objects with numerical cataloguing systems.
  • Sets of names that are more likely to be used as hostnames, such as gods and goddesses or arbitrary words are usually not permitted, because other people may perceive it as the name of a full computer host, which may deserve more than one IP address.
  • Stuff from popular culture is usually not permitted due to licensing reasons.
  • Concrete objects or symbols thereof are preferred over abstract objects.


SSL Certificate

The SSL certificate for this site, was, unfortunately, a bit tricky. First, the facts:

  • SSL certificates protect domain names, not IP addresses.
  • Most forms of virtual hosting are name-based, not IP-based.
  • We wanted web browsers to be able to access the pages without server name indication (SNI), since SNI was created mainly due to the IPv4 shortage.
  • We may possibly need multiple or wildcard SSL certificates due to the number of domain names involved.

As a result, we relied on the fact that IP addresses can be used to encode information and came up with the "geo $server_addr" hack, as shown below.

However, since the SSL certificate is still shared between all "hosts", the model reduces down to a series of separate web server hosts all sharing the same wildcard certificate, as may be found in an enterprise.

In that case, we recognize that in such a circumstance, traffic to one server could be maliciously redirected to another, and it would not be noticed because the other host's SSL certificate is still valid for the original domain name. The common way to fix this is by checking the Host header on all trusted systems; however, this is not yet implemented here.

IPv6 Things vs 42.be / 42.dnslabs.nl

It's noteworthy to point out that unlike https://42.be and https://42.dnslabs.nl, the server IP address is actually being used as a primary key. 42.be, on the other hand, appears to rely on the file path after the domain to retrieve the correct image, and specifying a different file path would result in a different image, even on the same domain. In our opinion, such a setup could just be a virtual or alias host of the same server, which would effectively nullify the overall intent of those sites.

Aliases 3000 and the jealousy of the Cloudflare nameservers

The i6t-cf-poc-nnn DNS records, exactly as they appear in my Cloudflare dashboard

After thinking about this a bit, I sort of realized that one factor as to why Aliases 3000 was included here was due to the Cloudflare nameservers. For those who don't know about this, when you sign up for Cloudflare and add your domain, you are given random nameservers of the form <some given name>.ns.cloudflare.com. Examples here. Each nameserver has its own IPv4 address. I guess they just wanted to express their riches through assigning give IPv4 addresses "names". But of course, to me, IPv4 is overrated, so I guess I should probably just be doing this entirely on IPv6.

Or maybe this was added because I wanted to playfully "mock" people who have firstname@lastname.example email addresses or firstname.lastname.example websites, as in "Why can they use 'charles' or 'robert' as a subdomain whereas I can't?" Maybe I'm just a bit strange; even this web app itself might be a bit strange due to how deep the subdomains are.

Speaking of Cloudflare, you can actually create an orange cloud CNAME or AAAA record pointing to any one of the subdomains and it should still show the correct site. AAAA records work in "flexible" and "full" SSL modes. CNAME records work in "full (strict)" mode.

("poc" means "proof of concept")

Note: Since my SSL mode is on "full (strict)", I had to add the following block to my http section. The only thing this does is serve the Cloudflare origin SSL certificate whenever Cloudflare connects to the server since Cloudflare always uses SNI to connect to the origin server. No other changes were made.

server {
        server_name "~^i6t-cf-poc-[0-9]+\.peterjin\.org$";
        listen [::]:443 ssl http2;
        ssl_certificate "/containers/nginx-40e/ssl/i6t-poc.crt";
        ssl_certificate_key "/containers/nginx-40e/ssl/i6t-poc.key";
        include /containers/nginx-40e/conf/normal-ipv6.conf;


The reason why it's https://bonnie1.aliases.peterjin.org and not "bonnie.aliases.peterjin.org" is because, internally, this domain is used as an NSEC/NXDOMAIN test. https://dnsviz.net/d/bonnie.aliases.peterjin.org/dnssec/

Note: Despite the my-bonnie-lies-over-the-ocean domains, there will never be a "bonnie2".

Implications on RIR number policies

Further information: IPv6 Things/Implications on RIR number policies

Due to the fact that IPv6 Things uses so many address gratuitously without any "duplication", the presence of systems like IPv6 Things could have significant implications on RIR number policies, specifically with respect to utilization criteria. Specifically, a hosting of IPv6 Things could potentially "inflate" IP address utilization in exponential time, while other factors remain constant. In other words, although there is only one Linux server system involved (a container in apps-vm5), 264 addresses are considered to be used (for a somewhat loose definition of use[6]) since they all respond to ICMPv6 pings and HTTP/HTTPS connections.

To put it another way, if this web app were to be made available on IPv4 (I hope this would never happen), would this, for the purposes of RIR number policy, serve as justification for a /16[7]? There is a possibility that such a justification could be abused, especially by large companies and ISPs, as a means of technically "using" address space without having the necessary infrastructure to expand their network, and the NRPM doesn't really differentiate between "mainstream" uses (as host IPs, NAT pools, or network infrastructure) and "frivolous" uses (IPv6 Things, Traceroute Text Generator, bad.horse, etc.).

Of course, there are probably a countless number of other ways to abuse utilization criteria, such as very large NAT pools.

Early binding vs. late binding

Many systems with multiple IP addresses differentiate between these addresses by binding to multiple specific addresses and using multiple sockets to differentiate between them; this is called "early binding". This app, however, only binds to a single address and uses getsockname() to differentiate between them in user space (as opposed to kernel space); this is called "late binding", and socketbox works pretty much on the same principle.

To do

  • The "aliases" group should be replaced with something else, distinct from Aliases 3000, since I have no intention of using most of the names in that zone in the current form of Aliases 3000. The current "Peter Jin a.k.a. (name)" format is inaccurate since it was mostly based on a simple formula that simply made the first letter of the name uppercase, and with an intended connection to an "expanded" version of Aliases 3000 instead of merely the names by themselves. Use subdomains of "names.misc.peterjin.org"
  • Remove the my-bonnie-lies-over-the-ocean-nn subdomains?
  • Should the IPv6 Bible be separated from this project? I think that it should be kept here, and just declare 2602:806:a003:40e:8000::/65 be reserved for major subprojects like for ones the scale of the IPv6 Bible.
  • The "aliases" group should be moved from 2602:806:a003:40e::300x:xx to 2602:806:a003:40e::300x:8xxx, with decimal instead of hexadecimal.

Source code

This section is outdated; see IPv6 Things/Configuration for current configuration.
ip route add local 2602:806:a003:40e::/64 dev lo
# nginx.conf snippet
http {
	# other configuration parameters not shown
	geo "$server_addr" $proxy_socket {
		default unix:/dev/null:;
		2602:806:a003:40e:b1be::/80 unix:/containers/nginx-40e/sockets/ipv6bible.sock:;
		2602:806:a003:40e::3000:0/100 unix:/containers/nginx-40e/sockets/aliases3k.sock:;
	geo "$server_addr" $ssl_cert_match {
		default things;
		2602:806:a003:40e:b1be::/80 ipv6bible;
	geo "$server_addr" $no_website {
		default 1;
		2602:806:a003:40e:b1be::/80 0;
	map "$time_iso8601" $my_time_simple_iso8601 {
		default "unknown";
		"~([0-9-]+)T([0-9:]+)\+00:00" "$1 $2";
	server {
		listen [::]:80 ipv6only=on;
		server_name .ipv6.bible .aliases.peterjin.org .rdns.peterjin.org;
		location / {
			return 301 https://$host$request_uri;
	server {
		listen [::]:80 default_server;
		include normal-ipv6.conf;
	server {
		listen [::]:443 ssl http2 default_server ipv6only=on;
		ssl_certificate "$ssl_cert_match.crt";
		ssl_certificate_key "$ssl_cert_match.key";
		include normal-ipv6.conf;
# normal-ipv6.conf
location = / {
	types { }
	default_type text/plain;
	if ($no_website) {
		return 200 "Host: $host\nCurrent date/time: $my_time_simple_iso8601 (UTC)\nYour IP address: $remote_addr\nServer IP address: $server_addr\n";
	proxy_pass http://$proxy_socket;
	proxy_set_header X-Server-IP "$server_addr";
	proxy_set_header Host "$host";
location / {
	if ($no_website) {
		return 404;
	proxy_pass http://$proxy_socket;
	proxy_set_header X-Server-IP "$server_addr";
	proxy_set_header Host "$host";
  1. These are not "subnets" as would be described in traditional computer networking; these are merely to describe the overall organization of IP addresses within 2602:806:a003:40e::/64.
  2. You might have heard people on YouTube "calling the SCP Foundation" with a number of 951-███-2602, and thinking that this IP address could be related to it since it also begins with 2602. This is a coincidence and I had no control over the first ten digits of my ARIN-assigned range. Neither was it the case that the IP addresses used to be a library or a film projector.
  3. Technically, if it were truly possible that a toaster (or other object) could only be referred to by the first person, then it wouldn't have a name.
  4. https://blog.cloudflare.com/revenge-listening-sockets/
  5. With the very limited exception of supporting the i6t-cf-poc domains on Cloudflare and the port 80 to 443 redirection (see #SSL Certificate for the reason behind this)
  6. A stricter definition of "use" in this context is that an IP address is used if it is not "unused" (e.g. an address not assigned to a host or a distinct service would not be considered to be "used"). This definition would result in 42,000 addresses being used, and can scale linearly (if a lookup table of discrete objects are used) or exponentially (if a number conversion algorithm is used)
  7. ARIN only issues up to a /20 for new LIRs without using the transfer market, so this technically would only apply to ARIN's "transfer pre-approval" service.
2602:806:a003:40e::/64 IPv6 Things (IPv6 Bible)
2602:806:a003:40f::/64 Traceroute Text Generator - Traceroute Maker
2602:806:a003:4ff::/64 GitLab