Berik Ashimov // senior.it.engineer
Download CV
POSTS / MIKROTIK

IPv6 on RouterOS 7 Without Fear: Prefix Delegation, RA, and a Firewall That Holds

// series · MikroTik RouterOS 7 Guide part 8 of 35
  1. ... 5 earlier
  2. WireGuard on RouterOS 7: Road Warriors, Site-to-Site, and Allowed-Address Discipline
  3. IKEv2 on RouterOS 7: Certificates, Mode-Config, and the Profile/Proposal/Policy Maze
  4. IPv6 on RouterOS 7 Without Fear: Prefix Delegation, RA, and a Firewall That Holds
  5. CAKE on RouterOS 7: Killing Bufferbloat Without a QoS Degree
  6. Multi-WAN on RouterOS 7: Recursive Failover, PCC Load Balancing, and When ECMP Is Enough
  7. ... 25 later
view the full series →

Most MikroTik routers I audit have IPv6 in one of two states: disabled entirely “for security,” or enabled with no firewall at all. The second is worse than it sounds — every device on the LAN gets a globally routable address, and without filter rules, globally routable means globally reachable. NAT was never a security feature, but it was an accidental one, and IPv6 removes the accident.

The disabled camp isn’t better off. Your devices still speak IPv6 to each other via link-local, your users still get IPv6 at every coffee shop, and you’ve just guaranteed that the first time it matters, nobody on your network understands it.

The fix takes maybe twenty lines: get a prefix from the ISP, address the LAN, advertise it, firewall it. Let’s do all four properly.

The Package Myth

First, housekeeping. On RouterOS 6, IPv6 was a separate package you had to enable, and half the internet’s MikroTik tutorials still open with “enable the ipv6 package.” On RouterOS 7 there is no separate IPv6 package — it ships inside the main routeros bundle. The relevant switch is:

Terminal window
/ipv6 settings print
# disable-ipv6: no <- this is what you want
/ipv6 settings set disable-ipv6=no

Note that on some builds where IPv6 was disabled, changes here take effect after a reboot. If /ipv6 menus exist and disable-ipv6=no, you’re live — stop looking for a package.

Getting a Prefix: DHCPv6 Client with PD

Residential and most business ISPs delegate a prefix via DHCPv6 Prefix Delegation — typically a /56 (256 LANs) or /48 if you’re lucky, a stingy /64 if you’re not. The client requests the prefix on the WAN interface and deposits it into an /ipv6 pool:

Terminal window
/ipv6 dhcp-client
add interface=ether1 request=prefix pool-name=isp-pd pool-prefix-length=64 \
add-default-route=yes use-peer-dns=yes disabled=no

Parameter by parameter: request=prefix asks for a delegated prefix (use request=address,prefix if your ISP also assigns a WAN address via IA_NA — many just use link-local for the WAN hop, which is fine and normal). pool-name is where the prefix lands; pool-prefix-length=64 means the pool hands out /64 chunks — one per LAN. add-default-route=yes installs the v6 default route toward the ISP.

Verify the lease:

Terminal window
/ipv6 dhcp-client print detail
# status=bound, prefix=2001:db8:1200::/56 (whatever your ISP gave you)
/ipv6 pool print
# dynamic pool "isp-pd" with your prefix, expire time = lease time

If it sits in searching, check that your IPv6 firewall permits DHCPv6 replies (UDP 546 from fe80::/10 — rule included below), and that the physical WAN actually has link-local connectivity: /ipv6 neighbor print should show the ISP router.

Addressing the LAN from the Pool

Never hardcode the delegated prefix — ISPs rotate them, especially after modem reboots. Pull from the pool instead:

Terminal window
/ipv6 address
add address=::1/64 from-pool=isp-pd interface=bridge-lan advertise=yes \
comment="LAN v6 from ISP delegation"

::1/64 means “take the next /64 from the pool and give this router the ::1 address in it.” advertise=yes is the important half: it tells the ND daemon to announce this prefix in Router Advertisements so hosts can autoconfigure. Multiple LANs? Repeat per bridge — each grabs its own /64 from the pool.

Terminal window
/ipv6 address print
# G flag = global, from-pool shown, actual address like 2001:db8:1200:0::1/64

Router Advertisements

SLAAC — hosts building their own addresses from an advertised prefix — is how the vast majority of IPv6 LANs run, and it needs only the RA. RouterOS defaults are workable, but be explicit about DNS, because a network where v6-only hosts can’t resolve names is a network that mysteriously “feels slow” (hosts try v6, time out, fall back):

Terminal window
/ipv6 nd
set [find default=yes] disabled=yes
add interface=bridge-lan advertise-dns=yes ra-interval=3m20s-10m \
managed-address-configuration=no other-configuration=no

advertise-dns=yes puts the router’s DNS into the RA (RDNSS option — modern OSes all honor it). The two flags are the knobs people misunderstand: managed-address-configuration=yes tells hosts “get your address from DHCPv6” (stateful — Android famously doesn’t implement it, so think twice); other-configuration=yes means “SLAAC your address but DHCPv6 for extra options.” For a normal network, both no — pure SLAAC plus RDNSS — is the least fragile combination.

Prefix-level parameters (lifetimes, autonomous flag) live under /ipv6 nd prefix and its default entry; the defaults — autonomous SLAAC, sane lifetimes — are correct for almost everyone.

Verify from a client, not just the router: a laptop on the LAN should have an address in your prefix, a default route via the router’s link-local address (that’s correct — v6 default gateways are link-local), and working ping -6 example.com.

Terminal window
/ipv6 neighbor print
# your LAN hosts appear here as they talk — the v6 ARP table

Privacy vs Stable Addresses

Expect every SLAAC host to hold multiple addresses, and don’t fight it. Modern OSes generate privacy addresses (RFC 4941) — temporary, rotated every day or so, used for outbound connections — alongside a stable address (RFC 7217 opaque interface IDs these days, not raw EUI-64 MAC-derived ones). Privacy addresses are why you can’t firewall LAN hosts by full /128 and expect it to stick.

Practical consequences: firewall by prefix or by device where the OS lets you disable temporary addresses (servers should use stable addresses only); for inbound rules to specific machines, either give the box a static suffix (2001:db8:1200:0::80 style, statically configured or via DHCPv6 reservation) or accept prefix-granularity. DNS registration on v6 LANs is its own project; for a handful of servers, static suffixes plus static AAAA records in /ip dns static is the pragmatic answer.

The IPv6 Firewall

The part you must not skip. No NAT means the filter chain is the only thing between the internet and your LAN hosts. These rules track MikroTik’s own defconf for v7 — the shape mirrors the IPv4 firewall from earlier in this series, with two v6-specific additions: ICMPv6 must flow (it carries ND and path MTU discovery — break it and you break IPv6), and DHCPv6 client replies must reach the router.

Terminal window
/ipv6 firewall address-list
add list=no_forward_ipv6 address=fe80::/10 comment="link-local"
add list=no_forward_ipv6 address=ff00::/8 comment="multicast"
/ipv6 firewall filter
# --- input: protect the router ---
add chain=input action=accept connection-state=established,related,untracked \
comment="established/related/untracked"
add chain=input action=drop connection-state=invalid comment="invalid"
add chain=input action=accept protocol=icmpv6 comment="ICMPv6 - do not block"
add chain=input action=accept protocol=udp dst-port=546 src-address=fe80::/10 \
comment="DHCPv6 client PD"
add chain=input action=accept protocol=udp dst-port=33434-33534 \
comment="UDP traceroute"
add chain=input action=drop in-interface-list=!LAN \
comment="drop everything else from WAN"
# --- forward: protect the LAN ---
add chain=forward action=accept connection-state=established,related,untracked \
comment="established/related/untracked"
add chain=forward action=drop connection-state=invalid comment="invalid"
add chain=forward action=drop src-address-list=no_forward_ipv6 comment="bad src"
add chain=forward action=drop dst-address-list=no_forward_ipv6 comment="bad dst"
add chain=forward action=drop protocol=icmpv6 hop-limit=equal:1 \
comment="RFC4890: drop ND leaking across router"
add chain=forward action=accept protocol=icmpv6 comment="ICMPv6"
add chain=forward action=drop in-interface-list=!LAN connection-state=new \
comment="drop new inbound from WAN"

Yes, that last forward rule means no unsolicited inbound to LAN hosts — the same posture NAT gave you on v4, but now explicit and auditable. Poke holes deliberately with accept rules above it (this is where those stable server suffixes earn their keep). And resist the urge to drop all ICMPv6 “to be safe”: too-big messages are how path MTU discovery works, and v6 has no router fragmentation to save you.

Verification under fire:

Terminal window
/ipv6 firewall filter print stats
# counters moving on established + icmpv6 = normal life
/log print where message~"drop"
# add log=yes log-prefix=v6drop to the final drops while tuning

Then test from outside: an online IPv6 port scanner against a LAN host’s address should show everything filtered.

Closing Thoughts

The complete recipe: disable-ipv6=no, one dhcp-client with request=prefix, one address from-pool per bridge with advertise=yes, RDNSS in the RA, and the firewall above. Twenty minutes, most of it verification.

What actually bites people, in order: missing the DHCPv6 UDP 546 rule (PD never binds), blocking ICMPv6 (PMTUD dies, big transfers hang), and hardcoding the delegated prefix (breaks on the next ISP re-lease). None of these are IPv6 being hard — they’re IPv6 being different.

The dual-stack failover story — what happens to v6 when your primary WAN drops — intersects with the multi-WAN article later in this series. Spoiler: it’s less polished than v4 and worth planning for.

// share