Tag Archive: IPv6

Am I running IPv6 on my network?



Setting up a routing protocol neighborship isn’t hard. In fact, it’s so easy I’ve made them by accident! How? There were already two OSPF neighbors in a subnet and I was configuring a third router for OSPF with yet another fourth router. But because the third router had an interface in that same subnet and I used the command ‘network area 0’ the neighborships came up. This serves as an example that securing a neighborship is not only to avoid malicious intent, but also to minimize human error.

Session authentication
The most straightforward way to secure a neighborship is adding a password to the session. However, this is not as perfect as it should be: it doesn’t encrypt the session so everyone can still read it, and the hash used is usually done in md5, which can easily be broken at the time of writing. Nevertheless, a quick overview of the password protection for EIGRP, OSPF and BGP:

Router(config)#key chain KEY-EIGRP
Router(config-keychain)#key 1
Router(config-keychain-key)#key string
Router(config)#interface Fa0/0
Router(config-if)#ip authentication mode eigrp 65000 md5
Router(config-if)#ip authentication key-chain eigrp 65000 KEY-EIGRP

Router(config)#interface Fa0/1
Router(config-if)#ip ospf message-digest-key 1 md5
Router(config)#router ospf 1
Router(config-router)#area 0 authentication message-digest

Router(config)#router bgp 65000
Router(config-router)#neighbor remote-as 65000
Router(config-router)#neighbor password

Note a few differences. EIGRP uses a key chain. The positive side about this is that multiple keys can be used, each with his own lifetime. The downside: administrative overhead and unless the keys change every 10 minutes it’s not of much use. I doubt anyone uses this in a production network.

BGP does the configuration for a neighbor (or a peer-group of multiple peers at the same time for scalability). Although there’s no mention of hashing, it still uses md5. It works with eBGP as well but you’ll need to agree on this with the service provider.

OSPF sets the authentication key on the interface and can activate authentication on the interface, but here it’s shown in the routing process, as it’s likely you’ll want it on all interfaces. It would have been even better if it was possible to configure the key under the routing process, saving some commands and possible misconfigurations on the interfaces. OSPF authentication commands can be confusing, as Jeremy points out. However:

OSPFv3 authentication using IPsec
The new OSPF version allows for more. Now before you decide that you’re not using this because you don’t run IPv6, let it be clear that OSPFv3 can be used for IPv4 as well. OSPFv3 does run on top of IPv6, but only link-local addresses. This means that you need IPv6 enabled on the interfaces, but you don’t need IPv6 routing and there’s no need to think about an IPv6 addressing scheme.

Router(config)#router ospfv3 1
Router(config-router)#address-family ipv4 unicast
Router(config-router-af)#area 0 authentication ipsec spi 256 sha1 8a3fe4a551b81dc24f6148b03e865b803fec49f7
Router(config)#interface Fa0/0
Router(config-if)#ipv6 enable
Router(config-if)#ospfv3 1 area 0 ipv4
Router(config-if)#ospfv3 bfd

This new OSPF version shows two advantages: you can configure authentication per area instead of per interface, and you can use SHA1 for hashing. The key has to be a 40-digit hex string, it will not accept anything else. A non-hex character or 39 or 41 digits gives a confusing ‘command not recognized’ error. The SPI vaue needs to be the same on both sides, just like the key of course. The final command is to show optional BFD support.

EIGRP static neighbors
For EIGRP you can define the neighbors on the router locally, instead of discovering them using multicast. This way, the router will not allow any neighborships from untrusted routers.

Router(config)#router eigrp 65000
Router(config-router)#neighbor Fa0/0

Static neighbor definition is one command, but there is a consequence: EIGRP will stop multicasting hello packets on he interface where the static neighbor is. This is expected behavior, but easily forgotten when setting it up. Also, the routing process still needs the ‘network’ command to include that interface, or nothing will happen.

BGP Secure TTL
Small yet useful: checking TTL for eBGP packets. By default an eBGP session uses a TTL of 1. By issuing the ‘neighbor ebgp-multihop ‘ you can change this value. The problem is that an attacker can send SYN packets towards a BGP router with a spoofed source of a BGP peer. This will force the BGP router to respond to the session request (SYN) with a half-open session (SYN-ACK). Many half-open sessions can overwelm the BGP process and bring it down entirely.


Secure TTL solves this by changing the way TTL is checked: instead of setting it to the hop count where the eBGP peer expects a TTL of 1, the TTL is set to 255 to begin with, and the peer checks upon arrival of the packet if the TTL is 255 minus the number of hops. Result: an attacker can send spoofed SYN packets, but since he’ll be more hops away and the TTL can’t be set higher than 255, the packets will arrive with a too low TTL value and are dropped without any notification. The configuration needs to be done on both sides:

Router(config)#router bgp 1234
Router(config-router)#neighbor remote-as 2345
Router(config-router)#neighbor ttl-security hops

These simple measures can help defend against the unexpected, and although it’s difficult in reality to implement them in a live network, it’s good to know when (re)designing.

I regularly see questions about IPv6 subnetting (despite the fact that the answers are out there with a simple Google search). ‘How do you subnet in IPv6?’ Well, there are some general guidelines that can help you a long way.

An IPv6 address is 128 bits, so prefix size can vary between /0 and /128. I’m going to list prefix sizes and their meanings below here, but remember, these are guidelines, and in no way mandatory. If you want to make a /103 subnet, you can do that.

  • A /64 is considered a basic subnet in IPv6. Reason for this is Stateless Address Autoconfiguration, or SLAAC (covered in RFC 4291). This is a method in which an IPv6 host forms his own address using the MAC address and the /64 prefix advertised by a router in the broadcast domain. It only works on /64, hence why you will usually see a lot of /64 subnets in an IPv6 network. Visually they are easy too: the first four sedets (there’s no definitive naming convention how to call a group of 16 bits in an IPv6 address) are the network part, the last four sedets the host part.
  • A /127 is a point-to-point link, as IPv6 has no concept of network and broadcast addresses. While many use a /64 on point-to-point links as well so the routing table looks simple, a /127 can more secure to work with.
  • A standard network with subnets is a /56, which gives 256 subnets (eight bits), each a /64. This is usually enough for a small to even medium network and should be considered a minimum assigned address space by an ISP for one connection (RFC 6177 refers to this best-practice).
  • A more common assignment by a provider, and a former best-practice, is a /48. It makes the visual part of addressing easy again: the first three sedets are the globally routed prefix, the fourth sedet is used for internal subnetting, and the remaining four sedets are again the host part for the /64 subnets. A /48 prefix is the smallest prefix allowed by most carriers in a global BGP routing table. It can also be seen in large companies that efficiently use route summarization.
  • Anything bigger, /44, /40, /32, are globally assigned IPv6 address spaces to companies with their own Autonomous System number, just like IPv4 global address space. You’re not likely to see this outside a BGP routing table.
  • A /16 is only used so far for 2002::/16 (6to4 addresses), just like a FE80::/10 (link-local),  FF00::/8 (multicast) and FC00::/7 (unique-local) addresses. None of these should ever appear in a routing table.
  • Finally a /0 refers to ::/0, which is a default route, just like IPv4’s route.

As you may have noticed by now, subnetting in IPv6 is done for efficient route summarization, not conserving address space. Why would you? This is why a /103 subnet would not be necessary, although together with DHCPv6, you can perfectly make such a subnet work.

IPv6 RA Guard feature.

As promised an IPv6 post! The Cisco IPv6 workshop was a real eye-opener for me, with plenty of configuration examples, best-practices and highlighting important security issues. Something that had been lingering on my mind for some time was the fact that, in my IPv6 experiments so far, any device could send out a Router Advertisement (RA) if it wanted to, and it would automatically become the gateway for the subnet. No questions asked. Since I’m running my IPv6 router and tunnel on a virtual machine using 50 MB RAM, I became increasingly worried about the simplicity of a possible man-in-the-middle attack. Just set up a VM in a campus LAN, make it send out RAs, and capture all traffic. IPv6 is even preferred above IPv4 when a modern OS has the choice. That’s a big security hole which is hard to detect.

Turns out, when I asked my question, that Cisco had an answer ready for me: RA Guard, which works similar to DHCP Snooping for IPv4. RA Guard filters out any ICMPv6 type 134 messages (RA) coming from any port that is marked as untrusted. For the moment it seems to be available on Catalyst 6500 Series only, since software release 12.2(33)SXI4. A full configuration guide for various IPv6 security measures can be found on the Cisco website.

I was told that support for other platforms would eventually be possible. For now, a workaround exists using IPv6 access lists, but this was not recommended I was told, as this would be done in software, not in hardware. Nevertheless, since the issue exists in a present day network already, I tried configuring it.

Here’s the configuration, which uses a Port ACL:

WS-C3560-8PC#configure terminal
Enter configuration commands, one per line.  End with CNTL/Z.
WS-C3560-8PC(config)#ipv6 access-list RA-GUARD
WS-C3560-8PC(config-ipv6-acl)#sequence 3 deny icmp any any router-advertisement
WS-C3560-8PC(config-ipv6-acl)#sequence 6 permit ipv6 any any
WS-C3560-8PC(config)#interface FastEthernet0/5
WS-C3560-8PC(config-if)#ipv6 traffic-filter RA-GUARD in
WS-C3560-8PC#show ipv6 access-list
IPv6 access list RA-GUARD
deny icmp any any  router-advertisement sequence 3
permit ipv6 any any (24 matches) sequence 6

Quite simple and it turns out it works. FastEthernet0/5 had an IPv6 router behind it, sending RAs every 60 seconds. Strange fact: all IPv6 traffic was matched against the permit ACL as expected, but the RAs weren’t matched against the deny statement, nor the permit statement. No logging to be found, though the switch did drop them. My computers did not detect any IPv6 router. Disabling the traffic-filter and waiting a minute made the clients configure an automatic IPv6 address.

Unfortunately, I couldn’t do any throughput tests, so I can’t tell what the impact on CPU is, but if rogue RAs are really an issue in the network, it might be worth the increased CPU.

I’ve already set up an IPv6 tunnel on three platforms: Vyatta, Cisco and Windows Server. This time, the same on OpenBSD. I’m not going to repeat myself, so for details about an IPv6 tunnel and how to get one, check the IPv6 tunnel article. I’ll be using  the same example values again:
Local IPv6 subnet:  2001:0:0:1234::/64
Tunnel subnet: 2001:0:0:1235::/64, with ::2 on our side and ::1 on the other endpoint side.
IPv6 DNS: 2000::2000
Device IPv4 address:
Tunnel endpoint:
Gateway to ISP:

I assume routing and IPv4 is configured properly already, with IP’s on interfaces and a default route towards the internet. If not, you’ve missed part I. Before starting the IPv6 part, remember that you’ll be creating a tunnel over an existing IPv4 network, so make sure pf allows the tunnel. I’ve added the following rules in /etc/pf.conf:

pass out quick on em0 from to
pass in quick on em0 from to

You’ll need to pass both ipv6ip and icmp, but since it’s just one trusted IP address, I’m doing a general rule. Don’t forget to activate the rule with ‘pfctl -f /etc/pf.conf’!

Next, creating the tunnel interface. In OpenBSD this is a ‘gif’, generic interface. To make it persist between reboots, create a /etc/hostname.gif0 file, zero for the first tunnel interface. The following lines go in the file:

!ifconfig gif0 inet6 alias 2001:0:0:1235::2 2001:0:0:1234::1 prefixlen 128
!route add -inet6 default 2001:0:0:1235::1

The internal IP is automatically translated by my router, but this may not always be the case. If not, use your external IP. The prefix length in the second line is 128, which is advised in the tunnelbroker configuration sample, but I’m not sure why. It wouldn’t work with 64 though. Finally, the third line adds a default route into the tunnel.

At this point the tunnel is up and running, but from the OpenBSD only. The devices on the connected subnet are not aware an IPv6 router is present. For this, the OpenBSD will have to send router advertisements. First, configure an IPv6 address on the interface, by adding the following line to /etc/hostname.em1:

!ifconfig em1 inet6 alias 2000:0:0:1234::1 prefixlen 64

Next, do the actual advertisements using the rtadvd deamon. In /etc/rc.conf, find the ‘rtadvd_flags:NO’ and change the ‘NO’ to the interface(s) that need it enabled, e.g. em1. Then create the file /etc/rtadvd.conf’ and enter the following:


This advertises the /64 prefix on the interface. A lot of other options are possible, such as the other-config-flag and managed-config-flag for DHCP options and a IPv6 DNS server, but I will not go into detail about that now. Finally, keep in mind icmp is used for router advertisements and neighbor discovery (the ARP replacement), so you’ll need to allow these. In /etc/pf.conf:

pass out quick on em1 inet6 proto icmp6
pass in quick on em1 inet6 proto icmp6

Finally, add some rules based on what you want to filter, e.g. a general rule blocking everything IPv6 inbound, and allowing outgoing connection of any kind (for now):

pass out quick on gif0 inet6 from 2000:0:0:1234::/64 to any
block in on gif0

After this, surfing to ipv6.google.com is possible from any computer in the local subnet.

IPv6 routes with MP-BGP.

BGP is a typical CCNP/CCIP topic, as is IPv6. But a combination of the two is rarely mentioned. Yet, just as with IPv4, BGP is mandatory for internet access. For IPv6, MultiProtocol BGP or MP-BGP is used.


In the above sample network, both routers are part of a different AS. They have loopbacks configured with a subnet that has to be advertised by BGP. The link between the two is also IPv6. To configure MP-BGP you’ll need a reasonably up to date IOS: I’m using a 12.4. The config of the left router:

R1(config)#ipv6 unicast-routing
R1(config)#int lo0
R1(config-if)#ipv6 address 2001:0:0:10::1/64
R1(config)#int f0/0
R1(config-if)#ipv6 address 2001:0:1:1::1/64
R1(config-if)#no shut
R1(config)#router bgp 65001
R1(config-router)#no bgp default ipv4-unicast
R1(config-router)#bgp router-id
R1(config-router)#neighbor 2001:0:1:1::2 remote-as 65002
R1(config-router)#address-family ipv6
R1(config-router-af)#neighbor 2001:0:1:1::2 activate
R1(config-router-af)#network 2001:0:0:10::/64

Some details here. ‘ipv6 unicast-routing’ is obviously required to make IPv6 routing possible. The interfaces are configured with the appropriate addresses and BGP for AS 65001 is set up. ‘no bgp default ipv4-unicast’ is the first important command here: it changes BGP to support multiple protocols. ‘address-family ipv6’ is the second important command: it defines the IPv6 parameters. Just like with standard BGP, a ‘network’ command defines the network(s) to advertise. And just like standard BGP, the network has to be present in the local routing table or no advertisement will be made.

Note that I’m advertising a /64 subnet here. While this works, in practice most ISPs will not accept a prefix more specific than a /48. Also, for once the ‘exit’ command is different, though typing ‘exit’ will work just the same. Oh, and the ‘router-id’ command is because no IPv4 address has been defined, which normally determines the router-id.

The config of the right router:

R2(config)#ipv6 unicast-routing
R2(config)#int f0/0
R2(config-if)#ipv6 addr 2001:0:1:1::2/64
R2(config-if)#no shut
R2(config-if)#int lo0
R2(config-if)#ipv6 address 2001:0:0:20::1/64
R2(config)#router bgp 65002
R2(config-router)#no bgp default ipv4-unicast
R2(config-router)#bgp router-id
R2(config-router)#address-family ipv6
R2(config-router-af)#neighbor 2001:0:1:1::1 activate
% Specify remote-as or peer-group commands first
R2(config-router-af)#neighbor 2001:0:1:1::1 remote-as 65001
R2(config-router-af)#neighbor 2001:0:1:1::1 activate
*Mar  1 00:09:49.171: %BGP-5-ADJCHANGE: neighbor 2001:0:1:1::1 Up
R2(config-router-af)#network 2001:0:0:20::/64

A similar configuration. This time I’ve set the ‘neighbor remote-as’ command inside the ‘address family ipv6’ context. This does not seem to matter, as long as it is defined before the activation command. Just like BGP, MP-BGP uses TCP port 179 for the connection, which you can see with the following command:

R2#show tcp brief
TCB       Local Address           Foreign Address        (state)
64F460B8  2001:0:1:1::2.179       2001:0:1:1::1.26321    ESTAB

It also show that the left router (R1) initiated the connection to R2 on port 179. The result is visible with ‘show ipv6 route’:

B   2001:0:0:10::/64 [20/0]
via FE80::C000:3FF:FEA4:0, FastEthernet0/0

MP-BGP works and networks are propagated.

Interestingly, the IOS allows you to define IPv4 MP-BGP peers for IPv6 networks, accepting all commands. It even tries to set up a TCP connection, but this connection times out, looping forever and never establishing completely. I’m not sure why this is, as I had expected the TCP session to form but no information being exchanged because of missing next-hop IPv6 addresses. It seems IOS is a bit buggy on this part.

I used this information on the Cisco website as part of this example.

SDM templates on 3560/3750.

If you’ve ever read the CCNP SWITCH books or similar materials, you’ve seen the terms CAM and TCAM. If you haven’t, here’s a brief explanation: a Content Addressable Memory contains the MAC addresses mapped to their ports and VLANs for efficient switching, and the Ternary Content Addressable Memory contains layer 3 forwarding and access-list information for efficient routing (multilayer switching). Switches normally have a set number of CAM and TCAM entries. If the CAM capacity is reached, frames with new destination MAC addresses are flooded. If the TCAM capacity is reached, new entries are processed in software and will put a strain on the device’s cpu. The TCAM is often further divided into smaller parts, each doing a specific function (routing, QoS, access-lists,…).

The Cisco 3560 and 3750 switches have something extra in this regard: their CAM and TCAM table sizes can be modified. You can check the current table sizes with the ‘show sdm prefer’ command:

WS-C3560-24PS#show sdm prefer
The current template is “desktop default” template.  The selected template optimizes the resources in  the switch to support this level of features for 8 routed interfaces and 1024 VLANs.

number of unicast mac addresses:                            6K
number of IPv4 IGMP groups + multicast routes:      1K
number of IPv4 unicast routes:                                  8K
number of directly-connected IPv4 hosts:                 6K
number of indirect IPv4 routes:                                 2K
number of IPv4 policy based routing aces:                0
number of IPv4/MAC qos aces:                               0.5K
number of IPv4/MAC security aces:                           1K

To change the table sizes, you have to change the sdm template:

WS-C3560-24PS-S(config)#sdm prefer ?
access                        Access bias
default                       Default bias
dual-ipv4-and-ipv6   Support both IPv4 and IPv6
routing                       Unicast bias
vlan                            VLAN bias

Yes, you’ve read that right. ‘dual-ipv4-and-ipv6’ activates IPv6, which cannot be configured by default. So if you have a 3560 or 3750 switch model and the command ‘ipv6 unicast-routing’ isn’t supported, this is the solution. For the other templates, a brief explanation below:

  • Access: optimized for a few routed interfaces and many VLANs. Less unicasts (layer 2 and layer 3), more space for security and policy-based routing (PBR). This is one of the only templates to support hardware-assisted policy-based routing.
  • Default: the default template. A mix of unicast routes, connected and host routes.
  • Dual-ipv4-and-ipv6: further divided in default, routing and vlan. Default reserves less space for layer 2 unicast and allocates this to IPv6 routing and security entries. Where default still has a roughly 50-50 balance between IPv4 and IPv6, routing allocates even more space to IPv6 routing. Vlan assumes a dual-stack environment with end-hosts connected on many VLANs.
  • Routing: allocated most of the table space to IPv4 unicast routing entries. This template can hold enough routes to forward in hardware to support a non-BGP core switch in a large network, and perhaps even a partial BGP table.
  • Vlan: allocates most table space to layer 2 unicasts, while still leaving enough space for IPv4 inter-vlan routing in hardware. Ideal for large subnets, or virtualized environments with many MAC addresses.

I also tested if there was a difference between an 8-port and a 24-port 3560, but the table sizes where exactly the same in all templates. Using these templates increases flexibility and allows you to choose what should be treated in hardware the most, depending on where in the network a switch is deployed.

Update 07/12/2011: someone gave me a very useful command to check the TCAM usage: ‘show platform tcam utilization’.

This one’s for you, Chris.

I’ve read countless articles, comments, posts about IPv6 and ‘that there are x IPv6 addresses for every human/square meter/grain of sand… on earth’. Okay, but let’s go hypothetical now, make some assumptions, and try to predict when the IPv6 address pool will be depleted. It’s just for fun, don’t expect any educational value in this article.

I’m going to use powers of ten to keep it somewhat imaginable. The total number of possible combinations with 128 bits is 3.40*10^38.  However, some ranges are unusable for internet routable traffic, and reserved by IETF:

  • FE80::/10 – Link local addresses.
  • FC00::/7 – Unique local packets, somewhat resembling RFC 1918 addresses for IPv4.
  • FF00::/8 – Multicast addresses.
  • 2002::/16 – 6to4 addresses, which are more of a transition mechanism and not really host addresses.

Currently, only the 2000::/3 range is global unicast, but other ranges can be assigned so in the future. The currently assigned range alone gives 4.25*10^37 addresses. For simplicity, I’m going to assume that eventually the entire address range will be used, except 2002::/16 and the entire F000::/4 block. That should cover all current and future reserved assignments. That’s still 3.19*10^38 addresses.

It should be clear by now we will run out of useable MAC addresses much sooner than IPv6 addresses, but let’s leave that out of the equation here. Let’s continue with our 39-digit number. But, even if we assume that in the near future every mobile device has an IP address, and we assume that the number of mobile devices doubles (e.g. one smartphone, one tablet), then we have about 10 billion devices, according to Wikipedia. That’s 10^10, which is nothing next to 10^38. Even multiplying that number by 1000, to cover all servers, point-to-point links, multiple home computers, household devices with an IP (refrigerators, ovens, photo frames, you name it), the total number of cars,… It will ‘only’ count up to 10^13, which doesn’t even scratch the surface of the entire available address pool.

The answer doesn’t lie in counting the number of devices that could possibly have an IP address. But, so far, I have been assuming perfectly filled subnets, no wasted addresses. That’s certainly not going to happen in IPv6: the EUI-64 mechanism for stateless autoconfiguration of IPv6 addresses requires a subnet to have 64 bits. This means most, if not all subnets (except point-to-point links), will have a size of /64 in the future. Highly inefficient for the conservation of addresses. Using the values I assumed earlier, that gives me 1.73*10^19 subnets. So from a 39-digit number, we now have a more realistic 20-digit number.
Let’s try to deplete that by, for example, giving one /56 per house (recommended, but that’s not likely to happen, as described in detail already by network instructor Chris Welsh), and one /48 per company. I haven’t found any definitive numbers, but I think it’s a conservative assumption that there are about 3 people on average sharing one home in the world, with fewer in the Western world (about 2.5 on average) and more in other parts of the world. As far as companies go, I’ve only found one quote saying there were 56 million companies worldwide in 2004, without anything to back it up, but it’s the best I have.

Assuming 60 million companies in 2012 with a /48, and 7.2 billion people, with an average of three per household, for a total of 2.4 billion households with a /56, I can calculate:

  • A /56 is 8 bits, 256 subnets times 2.4 billion makes 614.4 billion.
  • A /48 is 16 bits, 65536 subnets times 60 million makes 3.93*10^12
  • Together, that’s 4.55*10^12 subnets used.

That still doesn’t touch the 10^19 value. However, we now have a number that represents the used number of subnets versus the whole population (and assuming the whole world has access to IPv6 technology and needs it, but hey, we’re making assumptions for the future, and I’m hoping the best for humanity). Dividing our number of used subnets by the population, we get roughly 632 of /64 subnets used per human on this planet.
1.73*10^19 total subnets, divided by 632 subnets per human, gives 27,37*10^15 humans on the planet before the subnets are depleted. That’s 27 million billion. At this point, I was going to take a chart about human population growth, but none of them make predictions past 20 billion.

Conclusion: unless we reach for the stars and spread our IPv6 address space over the entire galaxy (even our solar system wouldn’t do), or finally perfect nanotechnology and decide to give each nanobot his own IPv6 address, we will not run out of IPv6 addresses. Even with large error margins, there are simply too many addresses and too few things to give it to. ISPs, hand out those /56 already!

IPv6 firewalling on Vyatta.

A while ago, I described how to set up an IPv6 Tunnel, so you could experiment with IPv6 at home. But while I’ve set it up myself a dozen times by now, I rarely had the tunnel active for a long time. Reason: I’m not too happy about a connection without any sort of firewall applied. So in this post, I’m going to describe how to set up a basic IPv6 firewall on an interface in Vyatta. The Cisco-based solution might follow later. To be honest, I’m not even going to try the Windows Server based solution.

Vyatta allows for three directions to apply the rules: in, out and local. Local means traffic destined for the device itself. This is important: I do not have to define any entries in my rules concerning local packets, like routing protocols, LLDP and IPv6 neighbour discovery and router solicitation.

Also, I cannot just block all ICMPv6 packets. IPv6 routers cannot perform fragmentation, unlike IPv4. This means fragmentation has to be done by end nodes. An ICMPv6 type 2 datagram, “Packet too big”, has to be allowed through, otherwise connections will fail if a too big MTU value is used (because the end nodes never find out why it doesn’t work). ICMPv6 type 3 code 1, “Time exceeded” packets tell when fragments are lost during transit.  Finally, types 133-136 are for router solicitation and neighbour discovery, but are handled as local on the Vyatta. If you’re using other platforms, be aware that these types need to be allowed too.

Now for the real ‘firewalling’ part. I’m going to allow echo replies in, so I can ping outside devices. I will not be allowing echo requests in. Yes, that’s making me feel more secure, although it doesn’t have any added value anymore. I’m also letting IPSec through because I’m doing VPN experiments later on. Apart from that, UDP will be allowed, and TCP packets with the ACK bit set. This is similar to the keyword ‘established’ on a Cisco ACL, and will only allow return packets of active sessions. This is not the same as a stateful firewall, but it provides a basic defense against connection attempts. Here is the code:

reggle@vyatta# set firewall ipv6-name WANFW
reggle@vyatta# set firewall ipv6-name WANFW description “Firewall to block incoming connections from IPv6 Tunnel”
reggle@vyatta# set firewall ipv6-name WANFW default-action drop
reggle@vyatta# set firewall ipv6-name WANFW rule 5 protocol icmpv6
reggle@vyatta# set firewall ipv6-name WANFW rule 5 icmpv6 type packet-too-big
reggle@vyatta# set firewall ipv6-name WANFW rule 5 action accept
reggle@vyatta# set firewall ipv6-name WANFW rule 5 description “Must be allowed or MTU discovery will break”
reggle@vyatta# set firewall ipv6-name WANFW rule 10 protocol icmpv6
reggle@vyatta# set firewall ipv6-name WANFW rule 10 icmpv6 type pong
reggle@vyatta# set firewall ipv6-name WANFW rule 10 action accept
reggle@vyatta# set firewall ipv6-name WANFW rule 10 description “Allow ping replies”
reggle@vyatta# set firewall ipv6-name WANFW rule 15 protocol icmpv6
reggle@vyatta# set firewall ipv6-name WANFW rule 15 icmpv6 type time-exceeded
reggle@vyatta# set firewall ipv6-name WANFW rule 15 action accept
reggle@vyatta# set firewall ipv6-name WANFW rule 15 description “May cause fragmentation issues otherwise”
reggle@vyatta# set firewall ipv6-name WANFW rule 20 ipsec match-ipsec
reggle@vyatta# set firewall ipv6-name WANFW rule 20 action accept
reggle@vyatta# set firewall ipv6-name WANFW rule 20 description “Allow incoming IPSec connections”
reggle@vyatta# set firewall ipv6-name WANFW rule 30 protocol tcp
reggle@vyatta# set firewall ipv6-name WANFW rule 30 tcp flags ACK
reggle@vyatta# set firewall ipv6-name WANFW rule 30 action accept
reggle@vyatta# set firewall ipv6-name WANFW rule 30 description “Allow established TCP connections”
reggle@vyatta# set firewall ipv6-name WANFW rule 35 protocol udp
reggle@vyatta# set firewall ipv6-name WANFW rule 35 action accept
reggle@vyatta# set firewall ipv6-name WANFW rule 35 description “Allow stateless UDP”

That’s it. After that, do a ‘set interfaces tunnel tun0 firewall in ipv6-name WANFW’, followed by ‘commit’, and it should work. You can test this by trying to ping an IPv6 host (like ipv6.google.com), which should work (echo reply allowed), while a ping to your computer should fail (echo request not allowed). The latter you can test on this website. Don’t forget to save the configuration if it works!

How to set up an IPv6 tunnel.

In an effort to promote IPv6 a bit more, I’m going to discuss three methods to set up an IPv6 tunnel today.

But first: what is an IPv6 tunnel and why would you need it? An IPv6 tunnel is a tunnel that transports IPv6 packets over an IPv4-only network, which is useful if you, like me, have an ISP that doesn’t use IPv6 addresses yet. By setting up the tunnel you can connect your local IPv6 network with the rest of the IPv6 internet. After configuring it, you should be able to surf to the Google IPv6 site.

Before you can begin configuring, you’ll first need an IPv6 provider. I used Hurricane Electric, others prefer Sixxs. Both are free. After registering on the site, you’ll receive a /64 subnet which is yours to use, as well as some details about setting up the connection. Yes, this means you get more IPv6 addresses for free to use in your living room than there are IPv4 addresses in the entire world.

After you have received your prefix we can begin configuration. Note that you’ll also receive a tunnel prefix which is used to configure the tunnel endpoints, as well as an IPv6 DNS server (which will require a DHCPv6 server to run on the network, annoying, I know). To make things consistent over the three configurations I’ll list example values that will be used:
Local IPv6 subnet:  2001:0:0:1234::/64
Tunnel subnet: 2001:0:0:1235::/64, with ::2 on our side and ::1 on the other endpoint side.
IPv6 DNS: 2000::2000
Device IPv4 address:
Tunnel endpoint:
Gateway to ISP:

I’m going to give the configuration for three types of device/operating system: a (virtualized) Vyatta 6.1, a (virtualized) Windows Server 2008 R2, and a Cisco 2691 router in GNS3. It is also possible to configure the tunnel on other devices (even an Apple Airport), but I have not tested those. Also, the tunnel used is an ipv6ip tunnel which uses IP Protocol 41. Since I’ll be passing through a NAT device (the ISP gateway), one of the tunnel endpoints will be a private address, which will be translated by the NAT device. You may need to put the IP in DMZ to forward the tunnel properly, or in my case, to forward the ICMP keepalives properly. And finally: the tunnel endpoint does not necessarily have two ethernet interfaces: the tunnel can be send out of the interface the IPv6 subnet is on.

Using the Vyatta as an IPv6 endpoint works stable and throughput is good. The basic version is free and it barely uses CPU, even when virtualized and under load, which makes for a nice endpoint without the need for a dedicated device. The configuration in the Vyatta is as following:

vyatta@vyatta:~$ configure
vyatta@vyatta# edit interfaces tunnel tun0
vyatta@vyatta# set encapsulation sit
vyatta@vyatta# set local-ip
vyatta@vyatta# set remote-ip
vyatta@vyatta# set address 2001:0:0:1235::2/64
vyatta@vyatta# set description “IPv6 Tunnel”
vyatta@vyatta# exit
vyatta@vyatta# set protocols static interface-route6 ::/0 next-hop-interface tun0
vyatta@vyatta# edit interfaces ethernet eth0
vyatta@vyatta# set address
vyatta@vyatta# set address 2001:0:0:1234::1/64
vyatta@vyatta# set ipv6 router-advert prefix 2001:0:0:1234::/64
vyatta@vyatta# exit
vyatta@vyatta# set system gateway-address
vyatta@vyatta# commit

Unfortunately, Vyatta currently does not properly support DHCPv6, so you can’t advertise the IPv6 DNS server to hosts in the subnet. In a dual stack environment this doesn’t break anything as the hosts will query the known IPv4 DNS servers, and those respond with IPv6 addresses in their payload if needed.

Cisco IOS
I can’t configure IPv6 on my 2611 routers, apparently they don’t have enough flash memory to store the right IOS version. The 3560 I have does support it with the Advanced IP Services IOS, but I don’t have that one, so I’m really out of luck here.
Update September 18, 2011: the 3560 has IPv6 support with the IP Services IOS as the Advanced IP Services is no longer used for a 3560, but there’s no support for tunneling as it can only be done in software and puts a heavy load on the CPU.

I resort to GNS3, where I set up a router and connect it to the physical network. The configuration of the tunnel is as following:

Router#configure terminal
Router(config)#ipv6 unicast-routing
Router(config)#interface Tunnel0
Router(config-if)#description IPv6 Tunnel
Router(config-if)#no ip address
Router(config-if)#ipv6 address 2001:0:0:1235::2/64
Router(config-if)#tunnel source
Router(config-if)#tunnel destination
Router(config-if)#tunnel mode ipv6ip
Router(config)#ipv6 route ::/0 Tunnel0
Router(config)#interface FastEthernet0/0
Router(config-if)#ipv6 address 2001:0:0:1234::/64 eui-64
Router(config-if)#ipv6 nd prefix 2001:0:0:1234::/64
Router(config)#ip default-gateway

Note that, just like with the Vyatta, you have to tell the router which prefix to advertise over the subnet. I was unable to properly configure DHCPv6 so all hosts could get an IPv6 DNS server, despite best efforts. Either a command is not working as expected or I am doing it wrong, so for now, it will work just like the Vyatta, with hosts querying DNS by IPv4.

Windows Server 2008 R2
Despite that this is the only one of the three devices that has a GUI, most configuration on the Windows Server will be done through the command line as well. The netsh command allows you to manipulate the IP stack in detail, as shown in the following configuration:

C:\>netsh interface teredo set state disabled
C:\>netsh interface ipv6 add v6v4tunnel IPv6Tunnel
C:\>netsh interface ipv6 add address IPv6Tunnel 2001:0:0:1235::2
C:\>netsh interface ipv6 add route ::/0 IPv6Tunnel 2001:0:0:1235::1

The first command disables the build-in Teredo in Windows, which automatically tries to create an IPv6 tunnel in case such traffic is needed. If you care about security, I would recommend this command on all your Windows 7 computers.

Next, creating a gateway for the subnet. If you go to the Network & Sharing Center, choose Change Adapter Settings, you can give the network card facing the subnet a static IPv6 address, in this case 2001:0:0:1234::1/64. The gateway should be ::, DNS server 2000::2000.

Last, we make sure all hosts receive the DNS address. This requires the DHCP role installed on the Windows Server. If present, go to Server Manager, DHCP, and configure an IPv6 scope 2001:0:0:1234::/64. Next, in the scope options, add option 23, and fill in 2000::2000. Windows accepts this and does not give any warning, but I couldn’t get this to work without rebooting after this.

So, these are three methods to get your IPv6 tunnel working. I hope it is all clear, greetings!

Update October 18, 2011: the Cisco IOS and Vyatta configuration had a missing command: you need to configure a default-gateway, otherwise it will not know where to send the tunnel! Commands updated.