Setting up an IPv4/IPv6 dual stack for a small network

Tall Stack


The IPv6 era is closer than you think. We take a practical look at configuring the next-generation Internet protocol.

By Owen DeLong

Paul Binet, 123RF

Most of us are aware that IPv4 addresses are running out. However, you might not be aware of how close we truly are to the end. The Internet Assigned Numbers Authority (IANA) will probably run out of unused IPv4 addresses by the end of 2010.

However, the real story is a bit more complicated. IANA doesn't allocate addresses directly to individuals (or even corporations) other than five Regional Internet Registries (RIRs). Those RIRs, in turn, allocate and assign addresses to end-user and ISP organizations and, in some cases, other Internet Registries. The IANA runout is fairly predictable. They will eventually give one of each of the final large blocks (known as /8s) in the free pool to each RIR, and that will be the end of the IANA IPv4 free pool.

It's a lot harder to predict when the RIRs will run out. RIRs deal with a wide range of variables, including different-sized requests and spikes and lulls in the timing of requests. Generally, you can expect each RIR to run out approximately one year after they receive their last /8 from IANA.

As you probably know, a new version of the Internet Protocol, which includes a whole new IP address space, has been in the works for some time. Some routers, and a few ISPs already provide support for IPv6, and you can expect an increase in the need for everyday networks to support IPv6 as the address supply dwindles. In this article, I investigate issues related to configuring IPv6 and show how you can get your IPv6 network up and running - even if your ISP doesn't offer full IPv6 support.

Getting Started

Unlike the legacy protocol in wide use today (IPv4), which has only 32 bits and can only address a little more than 3 billion unique unicast hosts, IPv6 has 128 bits and can address more than 340 undecillion hosts. (Rather than make you scramble for a dictionary, an undecillion is a 1 followed by 36 zeroes - in other words, an unimaginably large number). Figure 1 might help you visualize the size difference.

images/mandm great lakes-.png

Figure 1: Relative size of IPv4 vs. IPv6 address spaces.

IPv6 offers several important benefits that aren't possible with a conventional IPv4 network, such as enhanced autoconfiguration and multicasting, improved quality of service options, and bigger payloads. As you might expect, along with these benefits are some risks. The complete IPv6 implementation process consists of a series of smaller configuration steps. The exact order of these steps might depend on your network configuration, so you might need to follow a slightly modified path, but the basic steps described in this article are as follows:

  1. Get basic IPv6 connectivity to your gateway (a.k.a. router).
  1. Put an IPv6 security policy in place (hosts or gateway).
  1. Add IPv6 to your host-facing network.
  1. Add IPv6 to your hosts (if it didn't come up automatically)
  1. Start enabling services on IPv6
    a. DNS (custom view and test support)
    b. IMAP/POP
    c. SMTP
    d. Web
    e. DNS (production deployment)

Even if your environment requires some slightly different steps, this article will provide a framework for getting your IPv6 network up and running.

Basic IPv6 Connectivity

The first step is to get basic IPv6 connectivity to your router. You will want to do this carefully, so you don't accidentally create a security problem. Unfortunately, the crackers are already dual-stack, and there are known IPv6 attacks in the wild. Ideally, you want your ISP to give you native dual-stack connectivity. In most cases, this won't happen anytime soon, but it is worth making the phone call to determine whether this option is available. If you get lucky, you can skip most of this section.

Assuming your ISP can't offer you direct IPv6 services, you'll need a tunnel from a cooperative provider. IPv6 tunneling is a technique that lets you embed IPv6 packets inside IPv4 packets, which means your IPv6 network can use the legacy Internet for point-to-point links to other IPv6 networks. (Don't worry, it's easier to implement than to explain.)

Several providers offer free IPv6 tunnels. My personal favorite is the IPv6 Tunnel Broker operated by Hurricane Electric [1], my employer. If you have to terminate your tunnel behind NAT, you might want to try the tunnel broker and adapter software available at SixXS [2].

Signing up for the tunnel brokers is pretty easy, and you should be able to get either a /64 (if you only need one network) or a /48 (if you have multiple networks) from them. In my examples, I'm using a /48. If you get a /48, chances are it will come with an additional /64 for the tunnel link anyway. When you're setting up your tunnel, the broker will ask you for the public IP address of your gateway. If you don't know what the address is, you might be able to find out from WhatIsMyIPAddress.com [3].

Once you have your assigned addresses for the tunnel and for your local network, the next step is to actually configure your router. The first thing you'll want to do is make sure you don't have accidental IPv6 connectivity to hosts that don't have IPv6 security policies on them. You can either do this by putting an appropriate ACL on your router, or by making sure IPv6 isn't enabled on any hosts by default until you're ready to turn it on.

Once you master the more complex address notation, ip6tables works pretty much like iptables. Listing 1 shows the iptables entries from one of my hosts and Listing 2 shows the ip6tables configuration.

Listing 1: iptables Configuration
01 *filter
02 :INPUT ACCEPT [0:0]
03 :FORWARD ACCEPT [0:0]
04 :OUTPUT ACCEPT [0:0]
05 :RH-Firewall-1-INPUT - [0:0]
06 -A INPUT -j RH-Firewall-1-INPUT
07 -A FORWARD -j RH-Firewall-1-INPUT
08 # Don't block Loopback
09 -A RH-Firewall-1-INPUT -i lo -j ACCEPT
10 # Permit Syslog
11 -A RH-Firewall-1-INPUT -d 192.0.2.2/32 -m state --state NEW -m udp -p udp --dport 514 -j ACCEPT
12 # Permit BFD (for OSPF)
13 -A RH-Firewall-1-INPUT -d 192.0.2.2/32 -m state --state NEW -m udp -p udp --dport 3784 -j ACCEPT
14 -A RH-Firewall-1-INPUT -d 192.0.2.2/32 -m state --state NEW -m tcp -p tcp --dport 3784 -j ACCEPT
15 # Permit ICMP
16 -A RH-Firewall-1-INPUT -p icmp --icmp-type any -j ACCEPT
17 # Permit ESP and AH (for IPSEC)
18 -A RH-Firewall-1-INPUT -p 50 -j ACCEPT
19 -A RH-Firewall-1-INPUT -p 51 -j ACCEPT
20 # Permit hosts on local LAN
21 -A RH-Firewall-1-INPUT -s 192.0.2.0/24 -j ACCEPT
22 # Permit established sessions already in state table
23 -A RH-Firewall-1-INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
24 # Permit SSH
25 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
26 # Permit DNS
27 -A RH-Firewall-1-INPUT -s 192.0.2.0/24 -m state --state NEW -m udp -p udp --dport 53 -j ACCEPT
28 -A RH-Firewall-1-INPUT -s 192.0.2.0/24 -m state --state NEW -m tcp -p tcp --dport 53 -j ACCEPT
29 # Rate Limit external DNS queries
30 -A RH-Firewall-1-INPUT -m state --state NEW -m udp -p udp --dport 53 -m limit --limit 30/minute --limit-burst 90 -j ACCEPT
31 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 53 -m limit --limit 30/minute --limit-burst 90 -j ACCEPT
32 # Permit TFTP
33 -A RH-Firewall-1-INPUT -m state --state NEW -m udp -p udp --dport 69 -j ACCEPT
34 # Permit SMTP
35 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 25 -j ACCEPT
36 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 587 -j ACCEPT
37 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 465 -j ACCEPT
38 # Permit MySQL
39 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT
40 # Permit DHCP/BootP
41 -A RH-Firewall-1-INPUT -m state --state NEW -m udp -p udp --dport 67 -j ACCEPT
42 -A RH-Firewall-1-INPUT -m state --state NEW -m udp -p udp --dport 68 -j ACCEPT
43 # Permit HTTP
44 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
45 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT
46 # Permit LPD and CUPS from local network
47 -A RH-Firewall-1-INPUT -s 192.0.2.0/24 -m state --state NEW -m udp -p udp --dport 111 -j ACCEPT
48 -A RH-Firewall-1-INPUT -s 192.0.2.0/24 -m state --state NEW -m tcp -p tcp --dport 631 -j ACCEPT
49 -A RH-Firewall-1-INPUT -s 192.0.2.0/24 -m state --state NEW -m udp -p udp --sport 111 -j ACCEPT
50 # Log failed packets
51 -A RH-Firewall-1-INPUT -j LOG
52 # Send back an administratively prohibited ICMP message for non-matching packets
53 -A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited
54 COMMIT
Listing 2: ip6tables Configuration
01 *filter
02 :INPUT ACCEPT [0:0]
03 :FORWARD ACCEPT [0:0]
04 :OUTPUT ACCEPT [0:0]
05 :RH-Firewall-1-INPUT - [0:0]
06 -A INPUT -j RH-Firewall-1-INPUT
07 -A FORWARD -j RH-Firewall-1-INPUT
08 # Don't block Loopback
09 -A RH-Firewall-1-INPUT -i lo -j ACCEPT
10 # Permit Syslog
11 -A RH-Firewall-1-INPUT -d 2001:db8:feed:face::2/128 -m state --state NEW -m udp -p udp --dport 514 -j ACCEPT
12 # Permit BFD (for OSPF)
13 -A RH-Firewall-1-INPUT -d 2001:db8:feed:face::2/128 -m state --state NEW -m udp -p udp --dport 3784 -j ACCEPT
14 -A RH-Firewall-1-INPUT -d 2001:db8:feed:face::2/128 -m state --state NEW -m tcp -p tcp --dport 3784 -j ACCEPT
15 # Permit ICMP
16 -A RH-Firewall-1-INPUT -p icmp --icmp-type any -j ACCEPT
17 # Permit ESP and AH (for IPSEC)
18 -A RH-Firewall-1-INPUT -p 50 -j ACCEPT
19 -A RH-Firewall-1-INPUT -p 51 -j ACCEPT
20 # Permit hosts on local LAN
21 -A RH-Firewall-1-INPUT -s 2001:db8:feed:face::0/64 -j ACCEPT
22 # Permit established sessions already in state table
23 -A RH-Firewall-1-INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
24 # Permit SSH
25 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
26 # Permit DNS
27 -A RH-Firewall-1-INPUT -s 2001:db8:feed:face::0/64 -m state --state NEW -m udp -p udp --dport 53 -j ACCEPT
28 -A RH-Firewall-1-INPUT -s 2001:db8:feed:face::0/64 -m state --state NEW -m tcp -p tcp --dport 53 -j ACCEPT
29 # Rate Limit external DNS queries
30 -A RH-Firewall-1-INPUT -m state --state NEW -m udp -p udp --dport 53 -m limit --limit 30/minute --limit-burst 90 -j ACCEPT
31 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 53 -m limit --limit 30/minute --limit-burst 90 -j ACCEPT
32 # Permit TFTP
33 -A RH-Firewall-1-INPUT -m state --state NEW -m udp -p udp --dport 69 -j ACCEPT
34 # Permit SMTP
35 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 25 -j ACCEPT
36 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 587 -j ACCEPT
37 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 465 -j ACCEPT
38 # Permit MySQL
39 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT
40 # Permit DHCP/BootP
41 -A RH-Firewall-1-INPUT -m state --state NEW -m udp -p udp --dport 67 -j ACCEPT
42 -A RH-Firewall-1-INPUT -m state --state NEW -m udp -p udp --dport 68 -j ACCEPT
43 # Permit HTTP
44 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
45 -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT
46 # Permit LPD and CUPS from local network
47 -A RH-Firewall-1-INPUT -s 2001:db8:feed:face::0/64 -m state --state NEW -m udp -p udp --dport 111 -j ACCEPT
48 -A RH-Firewall-1-INPUT -s 2001:db8:feed:face::0/64 -m state --state NEW -m tcp -p tcp --dport 631 -j ACCEPT
49 -A RH-Firewall-1-INPUT -s 2001:db8:feed:face::0/64 -m state --state NEW -m udp -p udp --sport 111 -j ACCEPT
50 # Log failed packets
51 -A RH-Firewall-1-INPUT -j LOG
52 # Send back an administratively prohibited ICMP message for non-matching packets
53 -A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited
54 COMMIT

Once you have secured your hosts, the next step is to get IPv6 running on your router. Each tunnel comes with inside and outside addresses. The outside addresses will be the public IPv4 addresses of the devices providing the tunnel end-points. In this example, I'll use 198.51.100.5 (ISP end of tunnel), and 203.0.113.89 (Local end of the tunnel) for the outside addresses. The inside addresses will be 2001:db8:930:7000::1/64 (ISP side) and 2001:db8:930:7000::2/64 (local side).

First, make sure you can ping the remote router from the local tunnel endpoint address. In JunOS, you can issue the following CLI command:

ping 198.51.100.5 source 203.0.113.89

Assuming a successful result, configure the tunnel as shown in Listing 3.

Listing 3: Configuring the Router
01 router> edit
02 [edit]
03 router# set system internet-options ipip-path-mtu-discovery
04 router# edit interfaces ip-0/0/0.0
05 [edit interfaces ip-0/0/0 unit 0]
06 router# set description "6in4 tunnel to Hurricane Electric Tunnel Broker"
07 [edit interfaces ip-0/0/0 unit 0]
08 router# set point-to-point
09 [edit interfaces ip-0/0/0 unit 0]
10 router# set tunnel source 203.0.113.89
11 [edit interfaces ip-0/0/0 unit 0]
12 router# set tunnel destination 198.51.100.5
13 [edit interfaces ip-0/0/0 unit 0]
14 router# set tunnel path-mtu-discover
15 [edit interfaces ip-0/0/0 unit 0]
16 router# set family inet6 mtu 1280
17 [edit interfaces ip-0/0/0 unit 0]
18 router# set family inet6 address 2001:db8:930:7000::2/64
19 [edit interfaces ip-0/0/0 unit 0]
20 router# show
21 description "6in4 tunnel to Hurricane Electric Tunnel Broker";
22 point-to-point;
23 tunnel {
24     source 203.0.113.89;
25     destination 198.51.100.5;
26     path-mtu-discovery
27 }
28 family inet6 {
29     mtu 1280;
30     address 2001:db8:930:7000::2/64;
31 }
32 [edit interfaces ip-0/0/0 unit 0]
33 router# top
34 [edit]
35 router# commit and-quit
36 commit complete
37 Exiting configuration mode
38
39 router>

If all went well and the tunnel is configured correctly at the tunnel broker side, you should now be able to send your first IPv6 datagrams:

ping 2001:db8:930:7000::1 source 2001:db8:930:7000::2

Assuming a successful result - congratulations, your router is now on the IPv6 Internet.

However, just getting a router online isn't all that useful to most people. The next step is to get IPv6 on (one of) your LAN segment(s). Start by configuring an IPv6 address for the router's interface on the LAN segment (Listing 4).

Listing 4: An IPv6 Address for the Router
01 router> edit
02 [edit]
03 router# edit interfaces ge-0/0/0.0
04 [edit interfaces ge-0/0/0 unit 0]
05 router# edit family inet6
06 [edit interfaces ge-0/0/0 unit 0 family inet6]
07 router# set mtu 1280
08 [edit interfaces ge-0/0/0 unit 0 family inet6]
09 router# set address 2001:db8:930:5ffe::1/64
10 [edit interfaces ge-0/0/0 unit 0 family inet6]
11 router# set address 2001:db8:930:5ffe::/64 eui-64
12 [edit interfaces ge-0/0/0 unit 0 family inet6]
13 router# set address 2001:db8:930:5ffe::/64 primary
14 [edit interfaces ge-0/0/0 unit 0 family inet6]
15 router# up
16 [edit interfaces ge-0/0/0 unit 0]
17 router# show
18 description "LAN Segment 1"
19 family inet {
20     mtu 1280;
21     address 192.168.0.1/24;
22 }
23 family inet6 {
24     mtu 1280;
25     address 2001:db8:930:5ffe::1/64;
26     address 2001:db8:930:5ffe::/64 {
27         eui-64;
28         primary;
29     }
30 }
31 [edit interfaces ge-0/0/0 unit 0]
32 router# commit and-quit
33 commit complete
34 Exiting configuration mode
35
36 router>

Notice that the family inet configuration shows up when I issue the show command. That configuration is the existing IPv4 configuration, which was not modified and continues to function as before. Only the IPv6-related information is added, under the family inet6 clause.

With this configuration, I now have enough in the router for hosts to be issued IPv6 addresses automatically (through stateless auto-configuration) and for those hosts to ping the router and/or each other using IPv6. However, to make things work for Internet access, you need to add some IPv6 routing information to the router. The easiest thing to do is simply define a default route (see Listing 5).

Listing 5: A Default Route
01 router> edit
02 [edit]
03 router# edit routing-options rib inet6.0 static
04 [edit routing-options rib inet6.0 static]
05 router# set route ::/0 next-hop 2001:db8:930:7000::1
06 [edit routing-options rib inet6.0 static]
07 router# show<
08 route ::/0 next-hop 2001:db8:930:7000::1;
09 [edit routing-options rib inet6.0 static]
10 router# commit and-quit
11 commit complete
12 Exiting configuration mode
13
14 router>

With the default route in place, I am ready to move along and get host(s) up and running with IPv6. In the rest of this article, I focus on getting various services up and running on IPv6 on a Linux server.

To start, I'll do a basic connectivity check and tour some useful IPv6 commands under Linux. The ping6 command invokes the classic Ping utility for IPv6 environments:

host% ping6 2001:db8:930:7000::1

The following command will show you your ipv6 addresses:

host% ip -f inet6 addr show

Another version of the ip -f inet6 command will show you your known neighbors (like IPv4 arp):

host% ip -f inet6 neigh show

2001:db8:930:5ffe::1 dev eth0 lladdr 2c:6b:f5:03:0a:20

Adding IPv6 Capabilities

Now that you have a host with basic IPv6 connectivity, and at least some form of IPv6 security in place, it is time to start adding IPv6 capabilities to network services. For the most part, this is easier than you might expect.

The following sections describe how to set up IPv6 services for DNS, IMAP and POP, SMTP, HTTP, and DNS. These important network services will take you a long way toward full IPv6 support, but keep in mind that most of the gotchas lie in in-house software, as well as log analysis, monitoring or provisioning systems that can't yet parse IPv6 addresses or store them in databases.

Creating a Limited DNS View

The next step is to set up a separate view for authorized DNS clients so you can use IPv6 services without affecting other clients. This alternative is slightly more difficult, but significantly better, than the use of separate names for IPv6 services. A separate view is more difficult because you have to maintain duplicate zone files and a separate configuration for the view, as well as an ACL for authorized clients. On the other hand, the benefit is that, because you can switch to production with minimal changes on the web server, you won't have issues with X.509 certificates for SSL/TLS-enabled services. As a result, this option provides a more true-to-life test environment.

All of the DNS examples in this article are based on BIND, but other name servers should have similar capabilities.

I'll start with creating a customized view in named.conf by assuming your existing domain is example.com and your named.conf file looks something like the file shown in Listing 6.

Listing 6: named.conf
01 options {
02 directory "/var/named";
03 dump-file "data/cache_dump.db";
04         statistics-file "data/named_stats.txt";
05         listen-on { any; };
06         listen-on-v6 { any; };
07 };
08
09 logging {
10     channel all {
11 syslog daemon;
12 severity debug;
13 print-time true;
14 print-severity true;
15 print-category true;
16     };
17 };
18
19 include "/etc/rndc.key";
20
21 zone example.com {
22     type master;
23     file "named.example.com";
24 };
25
26 zone 2.0.192.in-addr.arpa {
27     type master;
28     file "named.192.0.2-24.rev";
29 };

The first step is to add a list of hosts to include in the IPv6 test. These hosts will be the ones that can see the IPv6 versions of the zone file(s).

Before line 21 in Listing 6 that reads zone example.com {, insert the following:

acl v6_clients {
    192.0.2.147;
    192.0.2.159;
    192.0.2.223;
};

Next, you need to create multiple views of the zones. First, copy the named.example.com file to a new file called named6.example.com. Next, modify named.conf so it looks like Listing 7.

Listing 7: Adding IPv6 Support to named.conf
01 options {
02   directory "/var/named";
03   dump-file
04     "data/cache_dump.db";
05   statistics-file
06     "data/named_stats.txt";
07   allow-recursion {
08     any;
09   };
10   listen-on { any; };
11   listen-on-v6 { any; };
12 };
13
14 logging {
15   channel all {
16     syslog daemon;
17   severity debug;
18    print-time true;
19    print-severity true;
20    print-category true;
21   };
22 };
23
24 acl v6_clients {
25   192.0.2.147;
26   192.0.2.159;
27   192.0.2.223;
28 };
29
30 include "/etc/rndc.key";
31
32 view v6_test {
33   match-clients  {
34     v6_clients;
35   };
36
37   zone example.com {
38     type master;
39     file
40       "named6.example.com";
41   };
42
43   zone 2.0.192.in-addr.arpa {
44     type master;
45     file
46       "named.192.0.2-24.rev";
47   };
48
49   zone
50     0.3.9.0.8.b.d.0.1.0.0.2.in6.arpa {
51       type master;
52       file
53         "named.2001.db8.930-48.rev";
54   };
55 };
56
57 view default {
58
59   match-clients {
60     any;
61   };
62
63   zone example.com {
64     type master;
65       file "named.example.com";
66   };
67
68   zone 2.0.192.in-addr.arpa {
69     type master;
70       file
71         "named.192.0.2-24.rev";
72   };
73   zone
74     0.3.9.0.8.b.d.0.1.0.0.2.in6.arpa {
75       type master;
76       file
77         "named.2001.db8.930-48.rev";
78   };
79 };

In Listing 7, each view needs to contain the full set of domains that will be visible to clients that get assigned to that view. That's why both reverse zones are defined in both views. Note, however, that the reverse zones use the same master file in both views, so, they'll serve the same data. Only the example.com zone uses a different file.

You can go ahead and make the IPv6 reverse visible, because that won't have any harmful effects and it saves you time later.

A DNS AAAA record associates a domain name with an IPv6 address. At this point, you can add AAAA records to the v6 version of the example.com zone file (named6.example.com). The general form will look something like the following:

host   IN  A      192.0.2.153
       IN  AAAA   2001:db8:930:5ffe::99

Don't forget to update the serial number in your SOA. You do not need to keep the serial numbers of the two versions of the example.com zone file in sync. In fact, some sites use deliberate differences in the serial numbers as an easy marker for which version is visible to a particular client.

The next step is to reload your name server (do this from the name server) and see if this all works (do this from one of the hosts permitted in your v6_clients ACL).

Mail Service

Listing 8 shows the relevant portions of the dovecot.conf file for the Dovecot IMAP/POP3 server before I added IPv6 capabilities.

Listing 8: dovecot.conf
01 mail_location = mbox:~:INBOX=/var/mail/%u
02 mbox_dirty_syncs = yes
03 protocol imap {
04 }
05
06 protocol pop3 {
07 }
08 protocol lda {
09   postmaster_address = postmaster@example.com
10 }

The IPv4 and IPv6 configurations for Dovecot are similar, so assuming you are using the default and listening to any available address, Dovecot will bind to IPv6 on the first restart after IPv6 is enabled on the system.

If you're using a different application for IMAP, POP services, or both, your mileage may vary. As for SMTP, Listing 9 shows what the protocol-relevant portions of my sendmail.mc file looked like before adding IPv6.

Listing 9: sendmail.mc
01 dnl #
02 dnl #
03 dnl # enable ipv4 in sendmail:
04 dnl #
05 DAEMON_OPTIONS(`Name=MTA, Family=inet')
06 dnl #

A simple change will bring your SMTP service up to IPv6 readiness (Listing 10). Which line(s) you uncomment in Listing 10 depends on your configuration. For most systems, an IPv6 listener will listen to both IPv4 and IPv6. However, some systems, such as BSD, have a feature that requires you to use separate listeners for IPv4 and IPv6. If you're on a BSD system or something similar, you might need to uncomment both the v6 and the v4 versions.

Listing 10: IPv6 with SMTP
01 dnl # The following causes sendmail
02 dnl # to additionally listen on the
03 dnl # IPv6 addresses.
04 DAEMON_OPTIONS(`port=smtp, Name=MTA, Family=inet6')dnl
05 dnl #
06 dnl # enable both ipv6 and ipv4 in sendmail:
07 dnl #
08 dnl DAEMON_OPTIONS(`Name=MTA-v4, Family=inet')
09 dnl #

For Linux, you comment out the v4 line (using dnl at the beginning of the line) and add the v6 line. The names (MTA-v4, MTA) are not particularly important unless you reference them elsewhere in your configuration. In that case, it's just important to make sure they match and that you duplicate any required configuration elements that are MTA specific if you uncomment both.

HTTP (Web Server)

Apache makes it easy to migrate a named virtual host from an IPv4 single stack to a IPv4/IPv6 dual stack. First, the original IPv4 configuration:

Listen 192.0.2.7:80
<VirtualHost 192.0.2.7:80>
    ServerName www.example.com
...

And these lines enable dual-stack support:

Listen 192.159.10.7:80
Listen [2001:db8::400:7]:80
<VirtualHost 192.159.10.7:80 [2001:db8::400:7]:80>
   ServerName www.delong.com
...

Notice that it just takes an additional Listen statement for the additional IP address and an additional IP address spelled out in the VirtualHost statement, which points out one additional consideration for IPv6.

Because the customary notation of :<port> at the end of an IP address is not entirely compatible with the use of a colon (:) as a delimiter within the IPv6 address, the standard solution in most applications and libraries is to enclose the address portion of the address/port tuple inside square brackets, then follow it with a colon and the port number.

DNS - Putting It into Production

Once the other services are up and running, it's time to revisit BIND and move the IPv6 configuration from a test environment to production. First, no changes are required to any of the servers from the test environment, so, all you're really doing is changing DNS so that it tells everyone about your IPv6 capabilities.

NOTE: Now is the time to make sure the only differences between the IPv4 and IPv6 zone files are AAAA records:

diff <v4zone> <v6zone> | grep '^>' | grep -v '\s+AAAA\s+'

If you get any data from this command, you probably need to update your IPv6 zone file before you proceed.

Assuming you get a nice empty result set in the previous test (or you have applied the necessary updates), it is time to make the production change. First, save the current named.example.com file somewhere, in case you have to roll back. Next, replace named.example.com with the contents (or the actual file) named6.example.com. Make sure to update the serial number to something higher than the last serial number in the previous named.example.com. Finally, remove the v6 test view and, optionally, remove the zone wrapper from the remaining configuration. After completing these steps, the named.conf file looks like Listing 11.

Listing 11: named.conf - Going Live
01 options {
02   directory "/var/named";
03   dump-file
04     "data/cache_dump.db";
05   statistics-file
06     "data/named_stats.txt";
07   allow-recursion {
08     any;
09   };
10   listen-on { any; };
11   listen-on-v6 { any; };
12 };
13
14 logging {
15   channel all {
16     syslog daemon;
17   severity debug;
18    print-time true;
19    print-severity true;
20    print-category true;
21   };
22 };
23
24 include "/etc/rndc.key";
25
26 zone example.com {
27   type master;
28     file "named.example.com";
29 };
30
31 zone 2.0.192.in-addr.arpa {
32   type master;
33     file
34       "named.192.0.2-24.rev";
35 };
36 zone
37   0.3.9.0.8.b.d.0.1.0.0.2.in6.arpa {
38     type master;
39     file
40       "named.2001.db8.930-48.rev";
41   };

Conclusion

At this point, you've completed the basic migration of a small and simple environment to IPv6. In a more complex environment, a few additional steps will require consideration. For example, monitoring systems, provisioning systems, and any log parsers might need updates to accommodate IPv6 addresses in their respective databases and/or parsing libraries.

Because it's entirely possible the world will run out of IPv4 addresses before the end of 2011, now is a good time to start experimenting with the next-generation IPv6 protocol.

INFO
[1] Tunnel Broker: http://tunnelbroker.net
[2] SixXS: http://www.sixxs.net
[3] WhatIsMyIPAddress.com: http://whatismyipaddress.com
THE AUTHOR

Owen DeLong is an IPv6 Evangelist at Hurricane Electric and a member of the ARIN Advisory Council. He brings more than 20 years of experience as a network engineer with a strong background in Systems Administration. Owen often gives educational talks on IPv6-related subjects around the world. When he is not presenting, he is also a commercial pilot and a SCUBA instructor.