How to reserve an IPv6 prefix and update NPTv6 firewall rules

I’d like to reserve an IPv6 prefix and use NPTv6 for the downstream “router” that manages IKEv2 road-warriors. My current idea is:

  1. Assign an IPv6 address using the delegated pool on an interface that’s connected to the downstream router, but disable its advertisement
  2. Use a DHCPv6 client script to find that assigned address, extract its prefix and use it to update mangling rules

However, it relies on having an updated address by the time DHCPv6 client script is called.

Is this a valid approach?

Upon further rumination, I think I don’t need to reserve a prefix. Instead, I can just translate the whole delegated prefix to my ULAs. An overlap with SLAAC seems to be very unlikely if not impossible.

…Except that it needs to be a custom prefix, otherwise I don’t know how to write the action=dnpt mangling rule.

Not sure what you are trying to achieve, but here’s some resource.

A DHCPv6 Client script that:

  1. Reserves a subnet
  2. Sets up firewall rules for address translation

Ended up using the loopback interface to force RouterOS to reserve a subnet from the delegated prefix. DHCPv6 Client’s script is used to update the firewall rules as necessary. It’s a somewhat smart because it makes sure that allocated subnet has the same prefix as the newly delegated one. Should help if script gets called before changes are propagated throughout RouterOS.

/system/script/add name=nptv6

# argLoopbackInt: name of the loopback interface
# argWanPool: name of the WAN pool 
# argUlaPool: name of the ULA pool
# argManagedID: regex-escaped unique ID of the managed objects

:global argLoopbackInt
:global argWanPool
:global argUlaPool
:global argManagedID

/ipv6/pool
:local varWanPrefix [get value-name=prefix $argWanPool]
:local varUlaPrefix [get value-name=prefix $argUlaPool]

:global WaitAddress do={
    /ipv6/address
    :local varAddress
    :retry command={
        :set varAddress [get value-name=address [find interface=$1 (address in $2) comment~"$3\$"]]
    } delay=1 max=5
    :return $varAddress
}

:do {
    /ipv6/address
    :local varOldGuaPrefix [get value-name=address [find comment~"$argManagedID\$"]]
    :local varNewGuaPrefix [$WaitAddress $argLoopbackInt $varWanPrefix $argManagedID]

    :if ($varOldGuaPrefix != $varNewGuaPrefix) do={
        :log info "Set $varNewGuaPrefix <-> $varUlaPrefix"
        /ipv6/firewall/mangle
        set dst-prefix=$varNewGuaPrefix [find action=snpt comment~"$argManagedID\$"]
        set dst-address=$varNewGuaPrefix src-prefix=$varNewGuaPrefix [find action=dnpt comment~"$argManagedID\$"]
    }
} on-error={
    /ipv6/address
    remove [find comment~"$argManagedID\$"]
    add interface=$argLoopbackInt advertise=no from-pool=$argWanPool comment="Managed: NPTv6 / $argManagedID"
    :local varGuaPrefix
    :do {
        :set varGuaPrefix [$WaitAddress $argLoopbackInt $varWanPrefix $argManagedID]
    } on-error={
        remove [find comment~"$argManagedID\$"]
        :log error "Unable to allocate prefix from $varWanPrefix on $argLoopbackInt"
        :error ""
    }

    :log info "Add $varGuaPrefix <-> $varUlaPrefix"
    /ipv6/firewall/mangle
    remove [find comment~"$argManagedID\$"]
    add chain=postrouting action=snpt src-address=$varUlaPrefix src-prefix=$varUlaPrefix dst-prefix=$varGuaPrefix comment="Managed: NPTv6 / $argManagedID"
    add chain=prerouting action=dnpt dst-address=$varGuaPrefix src-prefix=$varGuaPrefix dst-prefix=$varUlaPrefix comment="Managed: NPTv6 / $argManagedID"
}

/ipv6/dhpc-client/edit value-name=script

:if ($"pd-valid" = 1) do={
    :global argLoopbackInt "loopback"
    :global argWanPool <Pool added by DHCPv6 Client>
    :global argUlaPool <Pool reserved for ULA addresses>
    :global argManagedID "some-random-string"
    /system/script/run nptv6
}

Where loopback is just “/interface/bridge/add name=loopback”

Thank you for answering my ask (probably without reading it) at post #89 at Changing ipv6 prefix topic! You created quite useful scripts for home/SOHO environment.

Check this out too https://github.com/Kentzo/routeros-scripts-custom

Has some changes and bugfixed

netmap is now working perfectly fine in RouterOS, and you only need 1 rule per outgoing interface.