![]() |
| October 2001 | Get BSD | New to BSD? | Search BSD | Submit News | FAQ | Contact Us | Join Us |
|
By now, you've probably heard of the next generation Internet Protocol, IPv6. While it provides many improvements and new capabilities, the driving force behind its adoption is likely to be the much larger (and more flexible) address space that it defines. Continuing growth in the population of IP enabled devices has already put severe stress on address allocation and the routing infrastructure. The roll out of new enabling technologies such as 3G wireless and broadband to the home will predictably create a new wave of demand.
One way of dealing with these pressures is to use address translation technologies and accept the consequential degeneration and balkanisation of global connectivity. Another path is transition to a networking technology that can support the demands of today and tomorrow. In discussing this matter with my peers, I am often surprised by an ingrained reluctance to change, usually conjoined with an assertion that NAT solves the addressing problem. To me this is akin to defending the DOS 640K limit, and shows a marked lack of imagination about how we might be using the Net in ten or twenty years time.
The first step to using IPv6 is, of course, finding a suitable implementation. The good news is that, if you are using open source, chances are that you are already IPv6 ready. Current versions of {Free, Net, Open}BSD and Linux have a working version 6 stack out of the box. Solaris, AIX and MacOS X are reportedly in the same situation, whilst users of other flavours of Unix should check with their vendors. If you want to run IPv6 on your router appliance, you'll find that most major brands have the code either in beta test or ready for production.
IPv6 stacks are designed to cohabit peacefully with IPv4 (the "classic" version), and a router or host can use both concurrently. This is the first step in the migration to the new standard. There will never be a "flag day" when everyone switches to IPv6; rather, we will see a gradual increase in version 6 traffic over a number of years until it is the dominant form of internet datagram.
For instance, by using ping6(8) on my OpenBSD box, I can see that I am already running IPv6 without any special effort:
$ ping6 -c 5 localhost
PING6(56=40+8+8 bytes) ::1 --> ::1
16 bytes from ::1, icmp_seq=0 hlim=64 time=0.16 ms
16 bytes from ::1, icmp_seq=1 hlim=64 time=0.144 ms
16 bytes from ::1, icmp_seq=2 hlim=64 time=0.136 ms
16 bytes from ::1, icmp_seq=3 hlim=64 time=0.14 ms
16 bytes from ::1, icmp_seq=4 hlim=64 time=0.131 ms
--- ::1 ping6 statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/std-dev = 0.131/0.142/0.160/0.010 ms
All the examples in this article were produced using OpenBSD 2.9. The commands should be pretty much the same for all the BSDs (although your interface names and addresses will differ), whilst other Unix variants may require slightly different invocations. Check your local man(1) pages for details.
The next obvious step is to get two machines on your LAN communicating using IPv6. The machine on my desk, called lum, is equipped with a "rl" network card with the following configuration:
lum$ ifconfig rl0
rl0: flags=8843 mtu 1500
media: Ethernet autoselect (none)
status: active
inet 203.25.251.2 netmask 0xfffffff0 broadcast 203.25.251.15
inet6 fe80::260:67ff:fe06:19a5%rl0 prefixlen 64 scopeid 0x1
You can see that there is an IPv6 address configured for the interface, although it looks very different and perhaps a little awkward, compared to a classic quad number address. Don't worry - it will all start to make sense soon enough, and remember that in the real world we type in DNS names and not addresses anyway.
This address was automatically generated and configured when I enabled the network interface card, based on its underlying MAC address (yes, there is a standard for this). It is called a "link-local" address and is only valid on the network that the interface is directly connected to. The ability to auto-configure addresses, without engaging in a protocol such as RARP, is a powerful and robust bootstrap mechanism.
An IPv6 address is 128 bits long (that's right - no more running out of address space until we go extra-terrestrial), and is conventionally written as 8 lots of 16 bit hex numbers, separated by colons. You are allowed to have one instance of two consecutive colons to show that all the digits in between are zeroes. For instance, "::1", the loopback address, is the same as "0:0:0:0:0:0:0:1" but a lot easier to type.
IPv6 address come in many flavours, which can be distinguished by the first few bits of the address. A few common examples are:
| Prefix | Address type |
|---|---|
| 0000 0000 | Reserved |
| 001 | Aggregatable Global Unicast |
| 1111 1110 10 | Link-Local |
| 1111 1110 11 | Site-Local |
| 1111 1111 | Multicast |
Since link-local addresses are only good for a particular network, you have to specify the network interface as well. Conventionally, this is done with a trailing percent sign plus the interface name. So now the address "fe80::260:67ff:fe06:19a5%rl0" actually makes sense.
I have another machine, called gunbuster, with a "de" card on the same LAN. Let's check out the interface configuration:
gunbuster$ ifconfig de0
de0: flags=8863 mtu 1500
media: Ethernet autoselect (10baseT)
status: active
inet 203.25.251.1 netmask 0xfffffff0 broadcast 203.25.251.15
inet6 fe80::200:c0ff:fe4b:fdb%de0 prefixlen 64 scopeid 0x1
I can ping gunbuster from lum, using the existing link-local addresses. Note that I have to remember to tack lum's network card name to the end to gunbuster's link-local address, or I'll get a "no route to host" error.
lum$ ping6 -c 5 fe80::200:c0ff:fe4b:fdb%rl0
PING6(56=40+8+8 bytes) fe80::260:67ff:fe06:19a5%rl0
--> fe80::200:c0ff:fe4b:fdb%rl0
16 bytes from fe80::200:c0ff:fe4b:fdb%rl0, icmp_seq=0 hlim=64 time=0.445 ms
16 bytes from fe80::200:c0ff:fe4b:fdb%rl0, icmp_seq=1 hlim=64 time=0.44 ms
16 bytes from fe80::200:c0ff:fe4b:fdb%rl0, icmp_seq=2 hlim=64 time=0.437 ms
16 bytes from fe80::200:c0ff:fe4b:fdb%rl0, icmp_seq=3 hlim=64 time=0.441 ms
16 bytes from fe80::200:c0ff:fe4b:fdb%rl0, icmp_seq=4 hlim=64 time=0.443 ms
--- fe80::200:c0ff:fe4b:fdb%rl0 ping6 statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/std-dev = 0.437/0.441/0.445/0.003 ms
No one in their right mind would use a link-local address for anything but low level configuration and diagnosis. If you are using IPv6 solely within a given organisation, the next step you can take is to assign site-wide addresses to your nodes. As with IPv4, you can technically assign any addresses you like, but you would be smart to stick to the defined "site-local" scheme and ensure future interoperability. Site-local addresses are rather like the RFC-1918 addresses used internally by many organisations today, in that they are guaranteed to never clash with a globally routable address.
Site-local addresses are currently specified to be of the form "fec0:0:0:S:I:I:I:I", where "S" is the 16 bit subnet identifier, and "I:I:I:I" is the 64 bit node id. You can assign subnet and node identifiers as you see fit. There is a defined algorithm, for instance, for turning a 48 bit ethernet MAC address into a node id (which is the same one used for creating link-local addresses). However, I prefer to assign addresses manually, because I have only a few machines and I don't like addresses to change when I swap interface cards.
I can assign site-local addresses to my machines with a few simple commands:
gunbuster$ ifconfig de0 inet6 alias fec0::1
lum$ ifconfig rl0 inet6 alias fec0::2
lum$ ping6 -c 5 fec0::1
PING6(56=40+8+8 bytes) fec0::2 --> fec0::1
16 bytes from fec0::1, icmp_seq=0 hlim=64 time=0.557 ms
16 bytes from fec0::1, icmp_seq=1 hlim=64 time=0.444 ms
16 bytes from fec0::1, icmp_seq=2 hlim=64 time=0.439 ms
16 bytes from fec0::1, icmp_seq=3 hlim=64 time=0.429 ms
16 bytes from fec0::1, icmp_seq=4 hlim=64 time=0.421 ms
--- fec0::1 ping6 statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/std-dev = 0.421/0.458/0.557/0.050 ms
When you get sick of typing in raw addresses, you can map them to names in your /etc/hosts file or your DNS service. Modern implementations of named support the "AAAA" record type, which is used just like an "A" record only it holds an IPv6 address. Talk to your friendly DNS administrator for more information.
Before you can connect to other IPv6 sites, you need to understand the structure of "aggregatable global unicast" addresses, which are the types of addresses being allocated for wide area interconnectivity. It should be noted, however, that only 1/8 of the total possible address space is reserved for this format, leaving room for the future development of alternative global addressing schemes.
One problem that a huge address space does not address (and in fact could exacerbate) is that of routing table growth. Without some kind of topological structure, you potentially need a route for every destination in the universe which clearly does not scale well. As you might expect, aggregatable global unicast addresses deal with this problem by aggregating routes, in much the same way as is done today with the CIDR mechanism. Hence, addresses have a well defined structure:
| Format Prefix | TLA ID | Reserved | NLA ID | SLA ID | Interface ID |
|---|---|---|---|---|---|
| 001 | <13 bits> | <8 bits> | <24 bits> | <16 bits> | <64 bits> |
What are all these TLA's? Addresses are organised in a four tiered topological hierarchy:
Top level aggregator (TLA) identifiers are assigned to organisations at the top of the routing tree, typically major carriers and exchanges. There is currently room for 8192 such entities.
Next level aggregator (NLA) identifers are assigned by each TLA to create a routing architecture appropriate to their circumstances.
Site level aggregator (SLA) identifers are assigned by each end user organisation to create their private routing architecture.
Network interface identifiers are assigned to each host.
The TLA and NLA identifers are together referred to as the "public topology", since they define the collection of organisations which provide public transit services. The reserved bits between the TLA and NLA fields are there to allow for expansion of either or both in the future as demand and technical capacity dictates.
There may be additional structure imposed at the NLA and SLA level, created by the way that the identifiers are handed out. For instance, if a regional registry is given a contiguous block of NLA identifiers, it can break it up into sub-blocks for different carriers, who may in turn break their blocks up into smaller allocations again. However, the Interface ID level may not be broken up in this way as the various standards for generating these identifiers demand a 64 bit space.
To date, three TLA assignments have been made, each for a different purpose:
| Prefix | Use |
|---|---|
| 3ffe::/16 | 6bone experimental testbed allocation |
| 2001::/16 | Regional Internet Registry production allocation |
| 2002::/16 | 6to4 transitional address space |
In practice, each of these TLAs structures its subordinate address space differently:
| Prefix and TLA ID | pTLA ID | pNLA ID | SLA ID | Interface ID |
|---|---|---|---|---|
| 3ffe | <12 bits> | <20 bits> | <16 bits> | <64 bits> |
Historically, the pTLA field used to be 8 bits. Hence, as a special case, any pTLA identifier less than 0x0800 is treated as an 8 bit value, with the corresponding pNLA field lengthened to 24 bits. This piece of architectural ugliness would be bothersome except for the fact that the 6bone is supposed to be experimental, and the address space will be eventually reclaimed for other purposes. In practice, it has no impact unless you are a pTLA, and then it only complicates your routing tables a bit.
Production addresses are organised along similar lines to the 6bone space, although there has been some fine tuning. In addition, while the concept of a pseudo TLA has remained, it has been re-dubbed a "subTLA".
| Prefix and TLA ID | subTLA ID | Reserved | NLA ID | SLA ID | Interface ID |
|---|---|---|---|---|---|
| 2001 | <13 bits> | <6 bits> | <13 bits> | <16 bits> | <64 bits> |
The only problem is that, unless you are a major carrier, exchange or ISP, you probably won't qualify for a subTLA identifier of your very own. Which means that you have to wait for your ISP to get one, and for them to allocate part of their space to you. Or maybe for your ISP's ISP to get one, etc, etc. Now while this may, on the surface, suck somewhat, remember that this sort of disciplined hierarchy will keep the routing tables small enough to keep working. And that is a good thing for us all.
Meanwhile, if your ISP is trying to redefine "customer service" to mean "something that moves at the speed of flowing glass", then vote with your dollars. Failing that, join the 6bone until your part of the world catches up with the twenty-first century.
| Prefix and TLA ID | IPv4 address | SLA ID | Interface ID |
|---|---|---|---|
| 2002 | <32 bits> | <16 bits> | <64 bits> |
In the 6to4 scheme, when I want to send a version 6 datagram to a 2002::/16 prefix, I simply encapsulate it in a version 4 packet and send it to the IPv4 address embedded in the address. Clearly, this requires appropriate encapsulation software and routing at all 6to4 sites, plus sites who are willing to relay traffic between 6to4 and non-6to4 sites.
For those wishing to play with 6to4, the open source IPv6 stacks provide appropriate support out of the box.
Don't you ever wonder who bought the first telephone? Lord knows, that salesman deserved his commission! Early adopters of IPv6 face a similar problem. How do you build critical mass without a backbone, and how do you build a backbone without critical mass?
The answer is to build a virtual backbone, tunnelling IPv6 datagrams encapsulated in IPv4 packets. In other words, when two version 6 nodes wish to exchange traffic, they actually send version 4 packets whose payload is the IPv6 datagram. Each tunnel is a point to point link, and operates essentially as a physical connection.
With this technology, it is possible to establish a globally operational IPv6 network immediately, with native IPv6 physical links being established as traffic volume and performance demands.
The 6bone was established in exactly this manner, with the explicit goal of being a testbed for experimenters and early adopters. Right now, this is the best way to achieve global version 6 interconnection. Soon (hopefully), your ISP will be offering production address space along with supporting tunnels or physical links. Alternatively, you may like to experiment with the 6to4 mechanisms, although it is early days yet in that domain.
Joining the 6bone is quite straightforward:
Find an existing 6bone site to give you address space and connectivity. Usually this is a pTLA site, although it could just as easily be a pNLA site willing to slice off some of their SLA space. The current list of pTLAs can be found at the 6bone web site http://www.6bone.net/.
You should try and choose a neighbour that is close (in IPv4 terms) to you. For instance, the only pTLA operating in Australia at the time of writing was Trumpet Software, which makes the choice easy for people in that part of the world.
Set up the tunnel. On OpenBSD this is achieved using the gif(4) tunnelling interface:
gunbuster$ ifconfig gif0 giftunnel <my IPv4 addr> <remote IPv4 addr>
On my machine gunbuster, my tunnel configuration looks like this:
gunbuster$ ifconfig gif0
gif0: flags=8051 mtu 1280
physical address inet 203.25.253.124 --> 203.25.253.62
inet6 fe80::200:c0ff:fe4b:fdb%gif0 -> :: prefixlen 64 scopeid 0xe
As you can see, the link comes up with an appropriate link-local address. It doesn't know what the address of the other end is, so it just shows it as the wildcard address "::" (all zeroes). There is no need to ever assign global unicast addresses to either end of the link since the endpoints do not need to be globally addressable.
Set up the routing. Since we are an end node, we just point our default route at the tunnel:
gunbuster$ route add -inet6 default fe80::%gif0
How does this work? When the tunnel is created, a route for
fe80::%gif0 is auto-magically installed pointing to the virtual
link (you can query all of your routes via netstat(1)).
This saves us from ever having to know or rely on the link-local
address at our peer's site.
Define your internal address allocation and management policy, and assign global unicast addresses as appropriate. IPv6 addresses are configured on network interfaces in the usual manner:
gunbuster$ ifconfig de0 inet6 alias <IPv6 unicast address>
Test the link by contacting an external site:
gunbuster$ ping6 -c 5 gw.ip6.trumpet.net
PING6(56=40+8+8 bytes) 3ffe:8001:15:101::1 --> 3ffe:8000:1::1
16 bytes from 3ffe:8000:1::1, icmp_seq=0 hlim=254 time=254.175 ms
16 bytes from 3ffe:8000:1::1, icmp_seq=1 hlim=254 time=163.672 ms
16 bytes from 3ffe:8000:1::1, icmp_seq=2 hlim=254 time=162.032 ms
16 bytes from 3ffe:8000:1::1, icmp_seq=3 hlim=254 time=144.572 ms
16 bytes from 3ffe:8000:1::1, icmp_seq=4 hlim=254 time=163.788 ms
--- gw.ip6.trumpet.net ping6 statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max/std-dev = 144.572/177.648/254.175/38.940 ms
You can automate the tunnel configuration to occur at boot time by creating a /etc/hostname.gif0 file, containing something like this:
giftunnel <my IPv4 addr> <remote IPv4 addr>
!/sbin/route add -inet6 default fe80::%gif0
Don't forget to also add alias lines to your other interfaces' configuration files, so they are assigned the global unicast addresses that you choose.
When you have a working connection to the wider IPv6 network, the obvious question is: what can I use this for? I've already introduced ping6. As you might expect, there is a corresponding traceroute6, which will allow you to explore the topology of the network.
On an OpenBSD box, you will find that many network applications are already IPv6 enabled, including ftp(1), telnet(1) and ssh(1). All of these applications will use IPv6 if a version 6 address is specified or if the target has a "AAAA" DNS record.
Any server that is designed to work with inetd may be made available on the IPv6 network simply by adding an appropriate "tcp6" or "udp6" entry to /etc/inetd.conf. For instance, you can offer IPv6 time services by adding the line:
time stream tcp6 nowait root internal
Some IP stacks will listen for both version 4 and version 6 connections if a wildcard is specified. OpenBSD does not work like this, due to security concerns. As a consequence, a standalone server must be modified to listen on an IPv6 socket as well as (or instead of) an IPv4 socket. Patches for popular servers are becoming common.
As you begin to experiment with IPv6, it is important to watch out for security holes. Many servers have code that applies some form of address based security, and this may break in unexpected ways when handed an IPv6 socket. For instance, my anti-spamming code, which checks the incoming source address, failed miserably when I IPv6-enabled my SMTP server.
Similarly, many packet filters and security proxies are not IPv6 ready, and you may be creating ways to simply bypass your security infrastructure if you open up an IPv6 tunnel without sufficient forethought.
Did you ever wish that you were around when the Internet got started? This is a chance to be part of the complete re-engineering of the net, and its metamorphosis into something even larger and more ubiquitous than the original. Don't miss this opportunity, because the next one will be a long time coming...
6bone home page: http://www.6bone.net/
IETF draft-ietf-ngtrans-6bone-ptla-00: 6BONE pTLA and pNLA Formats
IETF draft-ietf-ngtrans-6to4-03: Connection of IPv6 Domains via IPv4 Clouds without Explicit Tunnels
OpenBSD home page: http://www.openbsd.org/
Provisional IPv6 Assignment and Allocation Policy: http://www.apnic.org/drafts/ipv6/ipv6-policy-280599.html
RFC 2373: IP Version 6 Addressing Architecture
RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
RFC 2722: 6bone Backbone Routing Guidelines