## Motivation

If you’re using one of MikroTik’s fine routers as an Internet gateway, you may at some point wish to enable IPv6 service on it. As of this writing in early 2024, we aren’t yet at the point where there are a lot of IPv6-only destinations on the Internet, but [here in the US at over half of all autonomous networks are IPv6-capable; moreover, the set of sites preferring that you contact them over IPv6 tracks that relentless up-and-to-the-right trend quite closely. This means the chances of a DNS lookup including an IPv6 address as the first entry — thus the first thing your Internet clients will try — is high and getting higher.

Enabling IPv6 is therefore a good idea for speed alone, as it avoids the time a client spends trying something that can’t work before falling back to good old IPv4.

With most modern ISPs, all you have to do to get a v4 address is to enable either DHCP or PPPoE. Alas, configuring IPv6 is not nearly as straightforward. Since the [RouterOS default configuration](/wiki?name=Default+Router+Configuration) doesn’t do any of it for you, I decided to write down the steps I took to get it all working here.


## Prefix Delegation

The first thing we must do is request a globally-routable IPv6 prefix(^You’d call that an assigned subnet in IPv4 terms.) of our very own from our upstream ISP. Xfinity allows us to do that on-demand using DHCPv6 from the gateway router via a process called [prefix delegation][PD].(^Contrast DHCPv4, which typically gets us only a single IP address lease.)

If the interface toward the cable modem is `ether1`, we can say:

``` config
/ipv6 dhcp-client
add interface=ether1 request=prefix \
pool-name=ipv6 use-peer-dns=no
```

The pool name is up to you.

[PD]:


## <a id="def-route"></a>Default Route

We’re choosing to accept RouterOS’s `add-default-route=no` default here because unlike with IPv4, IPv6 networks are supposed to auto-configure this via routing advertisements:

``` config
/ipv6 settings
set accept-router-advertisements=yes
```

Although this technique is sometimes referred to as [SLAAC], it is not a distinct protocol. RA messages are actually part of the “[NDP]” subset of the protocol. You will see it referred to by all of these names, plus MikroTik’s preferred “ND” initialism, which we’ll come across separately below. When searching the Internet for help, including “NDP” seems to give the best results, with second-best given by including “SLAAC”.

We have to change this setting because RouterOS configured as routers will ignore RA messages by default, doubtless from application of the precautionary principle: the local network admin might have a different plan. For a home gateway router, though, we’re not likely to have a better plan than to forward IPv6 traffic to the Internet by default. This setting tells the router to get that next-hop information from your ISP via RA messages instead of DHCPv6.

Only if [your local testing](#test) shows that this doesn’t work should you try setting `add-default-route=yes` on the DHCPv6 client.

Because enabling both methods might create conflicting routes, I recommend finding out which one works there on _your_ local slice of the Xfinity network, then stick with it. If both work, prefer the RA method.

[NDP]:
[SLAAC]:


## <a id="our-ip"></a>Assigning Ourselves an IPv6 Address

One of the options you have in RouterOS’s DHCPv6 implementation is to ask the provider to assign you an address as well as a prefix, making it work like good old DHCPv4. The problem I ran into with Xfinity is that they give you an address _outside_ that assigned prefix when you do that; this may have its uses, but it isn’t what I wanted. You might think that RouterOS would be smart enough to automatically assign itself an address from that “`ipv6`” pool, but as of version 7.14 at least, you have to ask for that explicitly:

``` config
/ipv6 address
add address=::1 from-pool=ipv6 interface=bridge
```

This shorthand notation says “statically reserve the first IP in the pool for the router.” (We’ll make profitable use of this special IP shortly.) By putting that IP on [the one-and-only LAN-side bridge][br], we’re effectively transferring that prefix’s assignment to our LAN.

Having done this, should show a new entry from your ISP’s IPv6 prefix assignment with the “G” (global) flag set and with “`ipv6`” as the pool name.

[br]: /wiki?name=Default+Router+Configuration#bridge


## <a id="dist-ip"></a>Distributing IPv6 Addresses to the Rest of the LAN

Whatever incentives your ISP may have to use DHCPv6, they are unlikely to apply inside your LAN, where the pure [SLAAC] alternative should suffice:

``` config
/ipv6 nd
set [ find default=yes ] interface=bridge
```

Enabling ND on the bridge implicitly enables SLAAC on the LAN.


## <a id="dns"></a>DNS

We set `use-peer-dns=no` above because we’re assuming the RouterOS default, where your border gateway router setup includes a caching DNS server of some type, and the other hosts on the network are told about it via DHCPv4. (One excellent reason to do this is to move to [DoH].)

There is nothing more we must do with DNS under that condition, but you _might_ want to add IPv6 addresses for your upstream DNS server(s) you use. For instance, if you’re using CloudFlare’s DNS server, you might want to add the corresponding `2606:4700:4700::1111` IPv6 address to your caching DNS setup.

Be clear on this, however: this is strictly optional. It is perfectly fine for a domain name request to come in on IPv4 but to give an IPv6 address. You don’t need to set up a separate caching IPv6 DNS server to get IPv6 AAAA record lookups working.

All that having been said, it is possible to advertise our caching DNS server’s _IPv6_ address like so:

``` config
/ipv6 nd
set [ find default=yes ] dns=2601:…::1
```

All this does is tell LAN clients wanting DNS service over IPv6 that they should contact our caching DNS server via this ::1 address, pasted in from the output, but without the “`/64`” on the end.

There’s no need to worry about this address changing on you for any cause short of switching to another ISP. One of the benefits stemming from the abundance of IPv6 is that delegated prefixes are assigned quasi-permanently, merely for the asking.

[DoH]:


## <a id="test"></a>Testing

With all these configuration steps done, if you now say it should yield something like this:

``` config
    DST-ADDRESS             GATEWAY                DISTANCE
DAc ::1/128                 lo                     0
DAg ::/0                    fe80::…:…:…:…2%ether1  1
DAc fe80::%ether1/64        ether1                 0
```

What we’re looking for here is the default route — `::/0` — and that it’s pointed toward our cable modem’s interface, `ether1` in this example. Because we’re showing an “RA” type route configuration, it has the “g” flag set on it, but if you had to resort to setting the route via DHCPv6, it will have the “d” flag set instead.

The next test is to say which should show something like this:

``` config
# INTERFACE  STATUS  REQUEST  PREFIX
0 ether1     bound   prefix   2601:…:…:…::/64, 2d14h28m50s
```

Key here is the “bound” status, meaning the delegated prefix is ours to use for the time duration shown. If we keep renewing our lease on-time, we should continue to get this same prefix indefinitely.

The penultimate test is via a 2606:4700:4700::1111` command. If all is well, you’ll get zero packet loss.

At that point, you can do a more full-featured check using one of the [many IPv6 test


## <a id="prefix-size"></a>Prefix Size

Our example output above shows that I’ve been granted a hefty `/64` slice of Comcast Cable’s massive [`2601::/20` IPv6 The exact prefix you get will vary from client to client, but the key thing to realize is that it means you’ve now got [approximately eighteen bazillion][18bz] publicly-routable IPv6 addresses to use inside your LAN. Aren’t _you_ the lucky one today?

No, not really. In a world where available IPv4 addresses are getting scarce enough that AWS is now [charging for them by the hour][AWSIP], IPv6 addresses are too plentiful to account for individually, as a practical matter. Indeed, the very reason ISPs give out such large blocks is because it actually makes their job easier: the bigger the address space they reserve to themselves, the larger their routing tables have to be. By giving you a whole `/64` all to yourself, they’ve fobbed off responsibility for that vast swath of their assigned address space to you. Doing this as a matter of policy distributes the load among all their other IPv6-using clients. An inexpensive MikroTik router isn’t a patch on a big ISP core router, but [a few dozen million of them][cstat] acting concurrently can collectively do far more routing work in cases like this, where you can distribute the workload.

[18bz]:
[AWSIP]:
[cstat]:


## <a id="mcast"></a>Multicast Interference

NDP is a protocol, which means that if you’ve got any kind of [IGMP/MLD snooping][msnoop] going on, there’s a fair chance you’ve inadvertently blocked the NDP messages. You need to either:

1. Disable MLD snooping entirely; or better,
2. Set `unknown-multicast-flood=yes` on the ports toward this ND source.

From a practical network management perspective, the second option works much like [DHCP snooping][dsnoop]: after enabling this type of filtering, you are likely to find yourself needing to establish paths where that traffic is explicitly allowed.

[dsnoop]:
[msnoop]:


## Conclusion

Well, so much for IPv6 being all about auto-configuration by default, eh?

I hope this guide has been helpful in getting you over the many potential stumbling blocks.


## <a id="license"></a>License

This work is © 2024 by Warren Young and is licensed under <a target="_blank" rel="license noopener noreferrer">CC BY-NC-SA 4.0<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" style="height:22px!important;margin-left:3px;vertical-align:text-bottom;"

<div style="height: 50em" id="this-space-intentionally-left-blank"></div>