Setting up Dual LAN with IPV6 DNS Caching

I had IPV6 running for some time using NextDNS, and I was using some of the nice adblocking features provided, but I really wanted to be using the IPV6 caching on the mikrotik router.

Attached is documentation and steps build while working with Claude.ai to synthesize the right set of commands for setting this up on your own:

MikroTik RouterOS 7.19.1 - IPv6 DNS Caching Configuration

Overview

This document describes how to configure a MikroTik router to act as an IPv6 DNS cache for LAN clients. By default, MikroTik's Neighbor Discovery (ND) advertises upstream DNS servers directly to clients, bypassing the router's DNS cache. This configuration changes that behavior so all IPv6 DNS queries go through the MikroTik's caching DNS server.

The Problem

When advertise-dns=yes is enabled in IPv6 ND, MikroTik advertises the IPv6 DNS servers configured in /ip dns directly to clients. This means:

  • IPv6 DNS queries bypass the router's cache

  • Queries go directly to upstream DNS servers

  • No local caching benefit for repeated queries

The Solution

  1. Assign a stable ULA (Unique Local Address) to each LAN interface

  2. Configure ND to advertise the router's ULA as the DNS server

  3. The MikroTik caches queries and forwards cache misses to upstream servers


Network Topology

Hardware

  • Router: MikroTik RouterOS 7.19.1

  • WAN: AT&T Fiber via sfp-sfpplus1

  • LAN1: bridge-lan1 (primary LAN for PCs)

  • LAN2: bridge-lan2 (secondary LAN)

  • LAN3: bridge-lan3 (SFP+ configuration network)

IPv6 Addressing Summary

Interface Global IPv6 Prefix ULA (DNS) Purpose
sfp-sfpplus1 2001:506:73a6:f7a::1/128 WAN uplink
bridge-lan1 2600:1700:3a61:6f0::/64 fd00::1 Primary LAN
bridge-lan2 2600:1700:3a61:6f1::/64 fd00::2 Secondary LAN
bridge-lan3 Management only

AT&T Prefix Delegation

AT&T provides a /60 prefix (2600:1700:3a61:6f0::/60), which yields 16 usable /64 subnets:

Subnet ID Network Assignment
0 2600:1700:3a61:6f0::/64 bridge-lan1
1 2600:1700:3a61:6f1::/64 bridge-lan2
2-f 2600:1700:3a61:6f2::/64 - 6ff::/64 Available

DNS Server Configuration

Upstream DNS Servers (NextDNS)

This guide uses NextDNS as the upstream resolver. You can substitute any DNS provider (Cloudflare, Google, Quad9, etc.).

Note: NextDNS provides profile-specific IP addresses linked to your account. Log in to my.nextdns.io and navigate to your profile's Setup tab to find your assigned addresses.

Protocol Primary Secondary
IPv4 45.90.28.x 45.90.30.x
IPv6 2a07:a8c0::xx:xxxx 2a07:a8c1::xx:xxxx

Alternative DNS Providers:

Provider IPv4 IPv6
Cloudflare 1.1.1.1, 1.0.0.1 2606:4700:4700::1111, 2606:4700:4700::1001
Google 8.8.8.8, 8.8.4.4 2001:4860:4860::8888, 2001:4860:4860::8844
Quad9 9.9.9.9, 149.112.112.112 2620:fe::fe, 2620:fe::9

View Current DNS Configuration

/ip dns print

Sample output:

                  servers: <your-ipv4-dns-1>
                           <your-ipv4-dns-2>
                           <your-ipv6-dns-1>
                           <your-ipv6-dns-2>
          dynamic-servers: 68.94.156.8
                           68.94.157.8
    allow-remote-requests: yes
               cache-size: 32768KiB
                cache-used: 6194KiB

Configure DNS Servers

Replace the placeholder addresses with your own DNS servers:

/ip dns set servers=<your-ipv4-dns-1>,<your-ipv4-dns-2>,<your-ipv6-dns-1>,<your-ipv6-dns-2> allow-remote-requests=yes

Example using Cloudflare:

/ip dns set servers=1.1.1.1,1.0.0.1,2606:4700:4700::1111,2606:4700:4700::1001 allow-remote-requests=yes

Key setting: allow-remote-requests=yes allows the MikroTik to act as a DNS server for LAN clients.


IPv6 Address Configuration

View All IPv6 Addresses

/ipv6 address print

Sample output:

Flags: D - DYNAMIC; G - GLOBAL, L - LINK-LOCAL
#    ADDRESS                       FROM-POOL  INTERFACE     ADVERTISE
0  G 2600:1700:3a61:6f0::fa82/64   att        bridge-lan1   yes
4 DL fe80::7a9a:18ff:fe9a:2620/64             bridge-lan1   no
5 DL fe80::7a9a:18ff:fe9a:2623/64             bridge-lan2   no
7  G fd00::1/64                               bridge-lan1   no
8  G 2600:1700:3a61:6f1::1/64      att        bridge-lan2   yes
9  G fd00::2/64                               bridge-lan2   no

Add ULA Address for DNS (bridge-lan1)

/ipv6 address add address=fd00::1/64 advertise=no interface=bridge-lan1

Parameters explained:

  • address=fd00::1/64 — Unique Local Address for DNS service

  • advertise=no — Don't advertise this prefix via SLAAC (clients won't get fd00:: addresses)

  • interface=bridge-lan1 — Assign to primary LAN bridge

Add Global + ULA Addresses (bridge-lan2)

/ipv6 address add address=2600:1700:3a61:6f1::1/64 interface=bridge-lan2 advertise=yes from-pool=att
/ipv6 address add address=fd00::2/64 advertise=no interface=bridge-lan2

Parameters explained:

  • from-pool=att — Use address from the AT&T delegated pool

  • advertise=yes — Advertise this prefix so clients get global IPv6 addresses


Neighbor Discovery (ND) Configuration

View Current ND Configuration

/ipv6 nd print

/ipv6 nd print detail

Sample output:

Flags: X - disabled, I - invalid; * - default
0  * interface=all ... advertise-dns=no dns=""
1    interface=bridge-lan1 ... advertise-dns=yes dns=fd00::1
2    interface=bridge-lan2 ... advertise-dns=yes dns=fd00::2

Understanding ND Entries

Flag Meaning
* Default entry
X Disabled
I Invalid

The interface=all entry applies to all interfaces unless overridden by a specific interface entry.

Configure ND for bridge-lan1

If an entry for bridge-lan1 already exists but is disabled:

/ipv6 nd enable [find interface=bridge-lan1]
/ipv6 nd set [find interface=bridge-lan1] dns=fd00::1 advertise-dns=yes

If no entry exists, create one:

/ipv6 nd add interface=bridge-lan1 dns=fd00::1 advertise-dns=yes

Configure ND for bridge-lan2

/ipv6 nd add interface=bridge-lan2 dns=fd00::2 advertise-dns=yes

Disable DNS Advertisement on Default Entry

To prevent the interface=all entry from advertising upstream DNS:

/ipv6 nd set [find interface=all] advertise-dns=no

Or by index:

/ipv6 nd set 0 advertise-dns=no


Complete Configuration Commands

bridge-lan1 Setup

# Add ULA address for DNS
/ipv6 address add address=fd00::1/64 advertise=no interface=bridge-lan1

# Enable ND entry for bridge-lan1 (if disabled)
/ipv6 nd enable [find interface=bridge-lan1]

# Configure ND to advertise router as DNS
/ipv6 nd set [find interface=bridge-lan1] dns=fd00::1 advertise-dns=yes

# Disable DNS advertisement on default entry
/ipv6 nd set [find interface=all] advertise-dns=no

bridge-lan2 Setup

# Add global IPv6 address from AT&T pool
/ipv6 address add address=2600:1700:3a61:6f1::1/64 interface=bridge-lan2 advertise=yes from-pool=att

# Add ULA address for DNS
/ipv6 address add address=fd00::2/64 advertise=no interface=bridge-lan2

# Create ND entry for bridge-lan2
/ipv6 nd add interface=bridge-lan2 dns=fd00::2 advertise-dns=yes


Verification Commands

MikroTik Router

Check IPv6 Addresses

/ipv6 address print

Check ND Configuration

/ipv6 nd print detail

Check ND Prefixes

/ipv6 nd prefix print

Check DNS Settings

/ip dns print

View DNS Cache

/ip dns cache print

Flush DNS Cache

/ip dns cache flush

Count Cached Entries

/ip dns cache print count-only

Check Interface Lists

/interface list print
/interface list member print

Check Bridge Ports

/interface bridge port print

Check DHCPv6 Client (WAN)

/ipv6 dhcp-client print detail

Check DHCPv6 Server (LAN)

/ipv6 dhcp-server print
/ipv6 dhcp-server option print


Client Verification

macOS

Check DNS Servers in Use

scutil --dns

Look for your ULA (fd00::1 or fd00::2) in resolver #1.

Check IPv6 Address

ifconfig | grep "inet6 2600"

Test DNS Resolution via Router

dig @fd00::1 google.com

Test AAAA Record Lookup

dig -6 -t AAAA @fd00::1 google.com

Test IPv6 Connectivity

ping6 google.com

Flush DNS Cache (macOS)

sudo killall -HUP mDNSResponder

List Network Services

networksetup -listallhardwareports

Windows

Check DNS Servers

ipconfig /all

Flush DNS Cache

ipconfig /flushdns

Test DNS Resolution

nslookup google.com fd00::1

Linux

Check DNS Configuration

cat /etc/resolv.conf

or

resolvectl status

Test DNS Resolution

dig @fd00::1 google.com

Check IPv6 Address

ip -6 addr show


Troubleshooting

Client Not Receiving fd00::1 as DNS

  1. Check ND configuration:

    /ipv6 nd print detail
    
    

    Verify dns=fd00::1 and advertise-dns=yes for your interface.

  2. Check for disabled entry (X flag):

    /ipv6 nd enable [find interface=bridge-lan1]
    
    
  3. Check default entry override: The interface=all entry may be advertising different DNS. Disable its DNS:

    /ipv6 nd set [find interface=all] advertise-dns=no
    
    
  4. Renew client network:

    • Toggle network interface off/on

    • Or flush DNS and wait for new Router Advertisement

DNS Queries Not Being Cached

  1. Verify allow-remote-requests:

    /ip dns print
    
    

    Must show allow-remote-requests: yes

  2. Check firewall rules:

    /ipv6 firewall filter print
    
    

    Ensure UDP/TCP port 53 is allowed from LAN.

Client Gets Global DNS Instead of ULA

DHCPv6 may be overriding ND. Check:

/ipv6 dhcp-server option print

If DHCPv6 option 23 (DNS) is configured with external servers, either:

  • Remove the option, or

  • Change it to advertise the ULA address

No IPv6 Connectivity on LAN2

  1. Check address assignment:

    /ipv6 address print where interface=bridge-lan2
    
    

    Must have a global address with advertise=yes.

  2. Check prefix advertisement:

    /ipv6 nd prefix print where interface=bridge-lan2
    
    
  3. Check pool availability:

    /ipv6 pool print
    
    

Why ULA for DNS?

Using Unique Local Addresses (fd00::/8) for DNS service provides:

Benefit Explanation
Stability ULA doesn't change when ISP prefix changes
Persistence Works even during ISP outages (for cached entries)
Simplicity Same address works regardless of ISP
No advertisement Clients don't get SLAAC addresses from ULA prefix

If AT&T changes your delegated prefix (e.g., after modem reboot), clients still reach the DNS cache at fd00::1 without reconfiguration.


Final Configuration Summary

ND Entries

# Interface DNS Advertise DNS Status
0 all no Active (default)
1 bridge-lan1 fd00::1 yes Active
2 bridge-lan2 fd00::2 yes Active

IPv6 Addresses

Interface Address Type Advertise Purpose
bridge-lan1 2600:1700:3a61:6f0::fa82/64 Global yes Client SLAAC
bridge-lan1 fd00::1/64 ULA no DNS service
bridge-lan2 2600:1700:3a61:6f1::1/64 Global yes Client SLAAC
bridge-lan2 fd00::2/64 ULA no DNS service

DNS Flow

┌─────────────┐     ┌─────────────────┐     ┌──────────────┐
│ LAN Client  │────▶│ MikroTik Cache  │────▶│ Upstream DNS │
│ (fd00::1)   │     │ (fd00::1)       │     │ (IPv4/IPv6)  │
└─────────────┘     └─────────────────┘     └──────────────┘
                           │
                           ▼
                    ┌─────────────┐
                    │ Local Cache │
                    │ (0ms reply) │
                    └─────────────┘


Document Information

  • Router OS Version: 7.19.1

  • Date Created: December 2025

  • Upstream DNS: Any IPv4/IPv6 capable DNS provider (NextDNS, Cloudflare, Google, Quad9, etc.)

  • ISP: AT&T Fiber (configuration adaptable to other ISPs with DHCPv6-PD)

The AI has generated a lengthy post with a lot of redundancies, but as usual with things involving AI and RouterOS, produces some really dumb parts too:

  • You don't need this:

    Because it produced two totally unneeded ECMP routes in your routing table.

  • What you should do is to go to https://www.unique-local-ipv6.com/, copy the random prefix presented to you (for example fdba:ed6f:78cf::/48) and use it to put a single IPv6 address on the lo interface:

    /ipv6 address
    add address=fdba:ed6f:78cf::1/128 advertise=no interface=lo
    
  • Then in the /ipv6 nd entries, just put that same single fdba:ed6f:78cf::1 as DNS server, you can even keep the one entry for all and put it there. No need for individual DNS addresses for each of your two bridges.

Really good feedback @CGGXANNX . I have updated my system and config it works as you recommend ! I agree that the additional ECMP routes are unnecessarily complex, and lo solution is far better.

Attached is tighter documentation which covers the implementation, shout if you think it still needs to be adjusted.

MikroTik RouterOS 7.x - IPv6 DNS Caching Configuration

Problem

By default, MikroTik's Neighbor Discovery (ND) advertises upstream IPv6 DNS servers directly to clients, bypassing the router's DNS cache.

Solution

  1. Generate a random ULA prefix at https://www.unique-local-ipv6.com/

  2. Assign a single /128 ULA address to the loopback interface

  3. Configure ND to advertise that address as DNS for all interfaces


Configuration

1. Configure Upstream DNS Servers

/ip dns set servers=<ipv4-dns-1>,<ipv4-dns-2>,<ipv6-dns-1>,<ipv6-dns-2> allow-remote-requests=yes

Note: Get your DNS server addresses from your provider. For NextDNS, find them at my.nextdns.io under Setup.

2. Add ULA Address on Loopback

Generate your prefix at https://www.unique-local-ipv6.com/, then:

/ipv6 address add address=fdXX:XXXX:XXXX::1/128 advertise=no interface=lo

Why /128 on loopback?

  • Single address serves all LANs

  • No ECMP routes in routing table

  • Always reachable, survives ISP prefix changes

Important: Don't use fd00:: — it's commonly used and risks collisions.

3. Configure ND to Advertise DNS

/ipv6 nd set [find interface=all] dns=fdXX:XXXX:XXXX::1 advertise-dns=yes

4. Disable Individual ND Entries (If Present)

/ipv6 nd disable [find interface!=all]

5. Add Global IPv6 to Additional LANs (If Needed)

For secondary LANs using ISP prefix delegation:

/ipv6 address add address=<prefix>::1/64 interface=<bridge-name> advertise=yes from-pool=<pool-name>


Verification

MikroTik

/ip dns print                              # Check DNS servers and allow-remote-requests=yes
/ipv6 address print where interface=lo     # Verify ULA address (/128)
/ipv6 nd print detail                      # Verify dns= and advertise-dns=yes
/ipv6 route print where dst-address~"fd"   # Should show single /128 route
/ip dns cache print                        # View cached entries
/ip dns cache flush                        # Clear cache for testing

Clients

OS Check DNS Test Resolution
macOS scutil --dns dig @<ULA> google.com
Windows ipconfig /all nslookup google.com <ULA>
Linux resolvectl status dig @<ULA> google.com

Flush client DNS: macOS: sudo killall -HUP mDNSResponder · Windows: ipconfig /flushdns


Troubleshooting

Issue Check Fix
Client not receiving ULA as DNS /ipv6 nd print detail Ensure advertise-dns=yes and dns= is set
DNS queries not cached /ip dns print Ensure allow-remote-requests=yes
/48 route instead of /128 /ipv6 route print Remove address, re-add with /128
DHCPv6 overriding ND /ipv6 dhcp-server option print Remove or update option 23

Example Configuration

# DNS with caching enabled
/ip dns set servers=1.1.1.1,1.0.0.1,2606:4700:4700::1111,2606:4700:4700::1001 allow-remote-requests=yes

# ULA on loopback (replace with YOUR prefix from https://www.unique-local-ipv6.com/)
/ipv6 address add address=fdXX:XXXX:XXXX::1/128 advertise=no interface=lo

# Advertise via ND (use same prefix as above)
/ipv6 nd set [find interface=all] dns=fdXX:XXXX:XXXX::1 advertise-dns=yes

:warning: Do not use fdXX:XXXX:XXXX — this is a placeholder. Generate your own unique prefix at https://www.unique-local-ipv6.com/ before running these commands.


RouterOS Version: 7.x · Last Updated: December 2025

@CGGXANNX - What’s your experience been with https://www.unique-local-ipv6.com/ I have another project where I am using OpenWRT to setup OpenVPN and have been fighting to get IPV6 working.

I could do the ULA setup from ULA addresses on either side of a VN tunnel – but I have also considered an IPV6 NAT , or even wilder – assigning a world-routable IPV6 to the VPN client across the tunnel (DHCPv6) … This is probably offtopic, but I am curious what you might suggest. Here’s the full project ( GitHub - beadon/OpenWRTOpenVPNMgmt: Openwrt VPN setup script, making management of Open VPN via CLI much simpler. )

By comparison, wireguard goes out and just sets a static IP address on either side to avoid the funny business of DHCPV6, DNS and ND playing together nicely ..

I don't use OpenVPN or IPsec, only WireGuard and SSTP (SSTP as backup for cases where I went to places where WG is blocked).

  • For my VLANs and SSTP and PPPoE servers, I normally use subnets from the prefix that my ISP gives to be (/64 subnet out of /60 or /56). A script is needed that is started by the DHCPv6 client, which updates all the places where the ISP prefixes is hardcoded, like address lists, firewall rules (mostly for netmap rules), routing rules, static DNS records, DHCPv6 server static bindings (so that I can give the DHCPv6 clients addresses in the ISP's prefix range too) by replacing the prefix part of the entries. With these no NAT is needed.

  • For WG peers I use free prefixes from https://tunnelbroker.net/. I don't use their tunnel service, but use the prefixes they give me like statically reserved GUA prefixes that can be sure to not be in conflict with remote hosts on the internet. On the WG clients I can hardcode the IPv6 address ranges, and they are 100% GUA. These GUA have higher precedent than IPv4, unlike the ULA prefixes, so my WG clients when using the tunnel for internet traffic will still prefer IPv6 over IPv4. On the router that acts as gateway, SRCNAT rules are required to map the Hurricane Electric prefixes to the ISP's prefixes. I use netmap SRCNAT rules with the script mentioned above doing the automatic update of to-address.

  • For the containers I mostly use static ULA prefixes, so that the containers have static addresses that can be statically referenced. If the containers require IPv6 internet access, then netmap rules are there to do the translation based on out-interface. I give some containers the Hurricane Electric static GUA prefix if I want them to prefer IPv6 when making outbound connections to the internet.

Thanks for this nice post, very useful.

A nice addition might be to show how when you use dig to verify the lookups are working, the first time might show something like Query time: 26ms to resolve, and if you repeat the same lookup a second time, it will show more like 1ms, thanks to it being cached.

Sure, easily done !

I did this when I was testing but forgot to include it in the instructions for the next person. I recommend testing the A records (IPV4) AND the AAAA (IPV6) records because the DNS at either IPV4 or IPV6 addresses will service BOTH. This is potentially confusing if you are expecting the IPV6 DNS provider to ONLY reply with AAAA records (it does both), or if you are expecting the IPV4 DNS nameserver to provide only IPV4 addresses (it does both!).

If you find you are missing dig, and are on MacOS, use brew ( https://brew.sh/) and install : brew install bind

MikroTik RouterOS 7.x - IPv6 DNS Caching Configuration

Problem

By default, MikroTik's Neighbor Discovery (ND) advertises upstream IPv6 DNS servers directly to clients, bypassing the router's DNS cache.

Solution

  1. Generate a random ULA prefix at https://www.unique-local-ipv6.com/

  2. Assign a single /128 ULA address to the loopback interface

  3. Configure ND to advertise that address as DNS for all interfaces


Configuration

1. Configure Upstream DNS Servers

/ip dns set servers=<ipv4-dns-1>,<ipv4-dns-2>,<ipv6-dns-1>,<ipv6-dns-2> allow-remote-requests=yes

Note: Get your DNS server addresses from your provider. For NextDNS, find them at my.nextdns.io under Setup.

2. Add ULA Address on Loopback

Generate your prefix at https://www.unique-local-ipv6.com/, then:

/ipv6 address add address=fdXX:XXXX:XXXX::1/128 advertise=no interface=lo

Why /128 on loopback?

  • Single address serves all LANs

  • No ECMP routes in routing table

  • Always reachable, survives ISP prefix changes

Important: Don't use fd00:: — it's commonly used and risks collisions.

3. Configure ND to Advertise DNS

/ipv6 nd set [find interface=all] dns=fdXX:XXXX:XXXX::1 advertise-dns=yes

4. Disable Individual ND Entries (If Present)

/ipv6 nd disable [find interface!=all]

5. Add Global IPv6 to Additional LANs (If Needed)

For secondary LANs using ISP prefix delegation:

/ipv6 address add address=<prefix>::1/64 interface=<bridge-name> advertise=yes from-pool=<pool-name>


Verification

MikroTik

/ip dns print                              # Check DNS servers and allow-remote-requests=yes
/ipv6 address print where interface=lo     # Verify ULA address (/128)
/ipv6 nd print detail                      # Verify dns= and advertise-dns=yes
/ipv6 route print where dst-address~"fd"   # Should show single /128 route
/ip dns cache print                        # View cached entries
/ip dns cache flush                        # Clear cache for testing

Clients

OS Check DNS Test Resolution
macOS scutil --dns dig @<ULA> google.com
Windows ipconfig /all nslookup google.com <ULA>
Linux resolvectl status dig @<ULA> google.com

Flush client DNS: macOS: sudo killall -HUP mDNSResponder · Windows: ipconfig /flushdns


Troubleshooting

Issue Check Fix
Client not receiving ULA as DNS /ipv6 nd print detail Ensure advertise-dns=yes and dns= is set
DNS queries not cached /ip dns print Ensure allow-remote-requests=yes
/48 route instead of /128 /ipv6 route print Remove address, re-add with /128
DHCPv6 overriding ND /ipv6 dhcp-server option print Remove or update option 23

Example Configuration

# DNS with caching enabled
/ip dns set servers=1.1.1.1,1.0.0.1,2606:4700:4700::1111,2606:4700:4700::1001 allow-remote-requests=yes

# ULA on loopback (replace with YOUR prefix from https://www.unique-local-ipv6.com/)
/ipv6 address add address=fdXX:XXXX:XXXX::1/128 advertise=no interface=lo

# Advertise via ND (use same prefix as above)
/ipv6 nd set [find interface=all] dns=fdXX:XXXX:XXXX::1 advertise-dns=yes

:warning: Do not use fdXX:XXXX:XXXX — this is a placeholder. Generate your own unique prefix at https://www.unique-local-ipv6.com/ before running these commands.


Results

Once configured, DNS queries through the router show caching in action:

First query (cache miss — fetched from upstream):

$ dig @<your-ULA> google.com

;; ANSWER SECTION:
google.com.   287   IN   A   142.250.189.14

;; Query time: 6 msec
;; SERVER: <your-ULA>#53

Second query (cache hit — served locally):

$ dig @<your-ULA> google.com

;; ANSWER SECTION:
google.com.   282   IN   A   142.250.189.14

;; Query time: 0 msec
;; SERVER: <your-ULA>#53

IPv6 AAAA record lookup:

$ dig -t AAAA @<your-ULA> google.com

;; ANSWER SECTION:
google.com.   280   IN   AAAA   2607:f8b0:4007:815::200e

;; Query time: 0 msec
;; SERVER: <your-ULA>#53

The 0 msec query time confirms the response came from the MikroTik's local cache rather than the upstream DNS server.


RouterOS Version: 7.x · Last Updated: December 2025