Listen to tutorial

In this tutorial we will learn how to deliver IPv6 to end clients using RouterOS/Mikrotik (PPPoE Concentrator) and log the IPv6 prefixes delivered in the WAN (Remote IPv6 Prefix Pool) and in the LAN (DHCPv6 PD Pool).

Even today we find some bugs in Mikrotik, and one of the biggest reasons for looking for this solution was that freeradius does not record the IPv6 PD logs (Mikrotik), the IPv6 that will be configured on the client LAN (the most important thing to be saved), would be much easier for many systems if they implemented Delegated-IPv6-Prefix, but it has been a subject of discussion on the forum for 6 years (Jan/2020) . And for a change, the solution is to give it that way…. (Popular gambiarra) 😛

To set up this log server you only need a WEB service (Apache/NGINX) PHP7+ and a MySQL/MariaDB database.

Requirement to set up a server from scratch :
– Debian 10 Stretch / Clean Installation
– How to create a WEB server Apache + PHP + MariaDB + phpMyAdmin in Debian 10 Buster “LAMP”

You need to have a little knowledge of IPv6 and know how to divide the subclasses. I recommend taking a look at there you will find NIC.BR courses.

I recommend you install an IPv6 calculator. Here is a very good calculator that I use on a daily basis.

123 # apt install wget python-ipaddr# wget -O /bin/ip6calc# chmod +x /bin/ip6calc

To do the calculation use e.g.:

1 # ip6calc 2001:db8:abcd::/48

For this project I developed some files in PHP where the logs that Mikrotik will send are “listened” and a simple screen to search for IPs in this database. I’ve already made it available on github .

With your web server configured, it would be nice to create a host in your DNS and configure a virtual domain on your web server, eg:, where you will place the project files. But as many don’t have this facility, I’ll try to leave the simplest way here, which is to place the files in the default Apache directory “/var/www/html”, so anyone can mount them 🙂

We access the base directory then

123456 # cd /var/www/html# wget apt install unzip# unzip mv LogIPv6RouterOS-master logs6# cd logs6

For security, create/edit a .htaccess to protect that only certain IPs have access. (despite having a token)

1 # vim .htaccess

Change the IPs you want to have access to. (Your router and your NOC)

12345678 Options -Indexes<RequireAll>    <RequireAny>        Require ip        Require ip        Require ip 2001:db8::/32    </RequireAny></RequireAll>

We will need to create a database and our table. To generate your passwords/token I recommend and generate something random.

1 # mariadb -p

Create the database, don’t forget to change the password (SUA_SENHA_DA_CONEXAO)


Import the table using the password created previously

1 # mariadb -u logs6 logs6 < logs6_mikrotik.sql -p

Now change the token in the config.php file to validate your data submission and access the simple interface I developed for searches, and the database connection password (db_password).

1234567891011121314 <?php/* Your security token  */$token = ‘xxxxxxxxxxxxx’; /* Set langage */// Portugês Brasil: pt-br// English:  en$lang = ‘pt-br’; /* Db conect */$db_host = ‘localhost’;$db_user = ‘logs6’;$db_password = ‘SUA_SENHA_DA_CONEXAO’;$db_name = ‘logs6’;

Now access your http(s)://URL/logs6/ . You should receive the following screen.

You can now authenticate with your token (you still won’t have logs, so there’s no point in searching lol)

If anyone has better ideas/criticisms, they are all welcome, feel free to create forks of the project.

To deliver IPv6 on your Mk, of course you need to have v6 connectivity on your PPPoE concentrator (I won’t go into detail on this part as it would go into the issue of routing your network, but if it’s worth a tip, an iBGP/OSPF solves it easily)

Server prepared to receive the logs, go to routerOS/Mikrotik. You will need to create two Pools, one for the WAN and the other for the LAN . As recommended by, for the client’s LAN we should send a /56 prefix (but this is not a rule). Calculating can help a lot in this part. In my example I will reserve a /48 prefix to deliver /64 prefixes on the WAN (65,536 /64 prefixes), and a /45 prefix delivering /56 prefixes , which would give 2,048 prefixes (clients).

Ex 2001:db8:1000::/48 (WAN)

Ex 2001:db8:2000::/45 (LAN)

IPv6 -> Pool

We change the profile. PPP menu Profiles tab. Remote IPv6 Prefix Pool select your pool_wan and DHCPv6 PD Pool your pool_lan

In the Open Protocol, Use IPv6 select yes.

Let’s jump into the Scripts tab . The idea here is every time a PPPoE connects or disconnects it executes a script. You will need to change the TOKEN that you configured in config.php , enter the correct URL (URLUP) that will find your log6.php file and in checkconnection enter the IP of your server (I explain the reason below).

123 :global TOKEN “xxxxxxxxxxxxxxxx”;:global URLUP “”;:global checkconnection “”;

The logic is: Before trying to send the information/values ​​(via post to log6.php) check if the connection exists with the log server, this was necessary when simulating a “power outage”, sometimes it can authenticate before getting a connection (if your user is on routerOS and then we lose all logs) in this case it waits for up to 5 minutes in the attempt (ping the ip checkconnection) before proceeding, if there is a connection (checkconnection) then proceeds with the script collecting the information.

I took advantage of collecting IPv4 as localAddr (NAS), remoteAddr (IPv4 delivered to the client) this will also facilitate an analysis if you want to match the data from freeradius (radacct), I have also taken the opportunity to capture the callerId (MAC), PPP service calledId . I believe that this information can be useful for other purposes. Following the script, all PPPoE that goes up will create a RemoteIPv6 (WAN) prefix and then I needed to do a while that I set to 60 seconds so that every second it tries to identify in /ipv6 dhcp-server binding if the client requested the prefix that it will automatically configure itself on your LAN, passing these requirements it will generate a LOG ( which you can also save on a remote log server ) and finally it will fetch the binding data if it succeeds or send null to the URL and so the server saves it in the database. You will see two logs on the logs screen, a blue one when a connection goes up and a red one when it disconnects, if you want to not have these logs just remove the line “ log warning message=….. / : log error message=…..

Access PPP, go to the profile tab and configure the profile scripts. Don’t forget to change global variables. On UP

123456789101112131415161718192021222324252627282930313233343536 {  :global TOKEN “xxxxxxxxxxxxxxxx”;  :global URLUP “http://________________/logs6/log6.php”;  :global checkconnection “”;  :local ii 0;  :local tt 300; # Aguarda até 5min para tentar  while ( $ii < $tt && ([/ping $checkconnection count=1]=0) ) do={    :put $ii    :set $ii ($ii + 1)    :delay delay-time=1s    :log error “Awaiting connection … $checkconnection”;  }  :local localAddr $”local-address”  :local remoteAddr $”remote-address”  :local callerId $”caller-id”  :local calledId $”called-id”  :local interfaceName [/interface get $interface name]  :local RemoteIPv6 [/ipv6 nd prefix get value-name=prefix [find interface=$interfaceName]]  :local i 0;  :local x 1;    :local t 60; # Segundos aguardando ipv6 ser configurado no cliente  while ($i < $t && [ :len [/ipv6 dhcp-server binding find server=$interfaceName] ] < $x) do={    :put $i    :set $i ($i + 1)    :delay delay-time=1s  }  if ($i = $t) do={    :log warning message=”UP: $user | $callerId | $calledId | $remoteAddr | $localAddr | $RemoteIPv6 | NULL”    /tool fetch url=”$URLUP” http-data=”action=i&token=$TOKEN&user=$user&mac=$callerId&nas=$localAddr&service=$calledId&ipv4=$remoteAddr&remoteipv6=$RemoteIPv6″ http-method=post  } else={    :local DHCPv6PD [/ipv6 dhcp-server binding get value-name=address [find server=$interfaceName]]    :log warning message=”UP: $user | $callerId | $calledId | $remoteAddr | $localAddr | $RemoteIPv6 | $DHCPv6PD”    /tool fetch url=”$URLUP” http-data=”action=i&token=$TOKEN&user=$user&mac=$callerId&nas=$localAddr&service=$calledId&ipv4=$remoteAddr&remoteipv6=$RemoteIPv6&dhcpv6pd=$DHCPv6PD” http-method=post  }  file remove log6.php}

And when disconnecting we will send it to the server that it discounted.
On Down

123456789 {  :global TOKEN “xxxxxxxxxxxxxxxx”  :global URLDOWN “http://________________/logs6/log6.php”  :local localAddr $”local-address”  :local callerId $”caller-id”  /tool fetch url=”$URLDOWN” http-data=”action=u&token=$TOKEN&user=$user&mac=$callerId&nas=$localAddr” http-method=post  :log warning message=”DOWN: $user | $callerId | $localAddr”  file remove log6.php}

To conclude in IPv6 -> ND

Check all options.

In the IP -> DNS menu , also enter the IPv6 DNS. Only then will the client learn DNSv6 ( Advertise DNS )

Now every PPPoE that authenticates a log will be recorded, the cool thing here is that if someone doesn’t use Radius they can also have a log 🙂

In the log6.php file that receives the logs whenever a user connects, it will check whether there is an open connection, because in cases of power outages there will not be time for it to inform that pppoe has disconnected, so it closes the connection with the time of the new connection, so this IP may have been used by another user, in this case it records the time that the connection closed with “failure” , so in the case of an investigation you can see if the same IP had not some other user who used it at the requested time.

Using phpMyAdmin it is also very easy to search.

For those who want to block an IPv6 client, a solution is to create another Pool6 in the Router with an “invalid” class, for example 2001:db8:6666::/48, and in freeradius use the attribute Mikrotik-Delegated-IPv6-Pool , it can be in the radreply table (saying for which user) or for a “flat” configuration group (radgroupreply).


Ex.: radreply

12 INSERT INTO `radreply` (`username`, `attribute`, `op`, `value`) VALUES(‘nome_user’, ‘Mikrotik-Delegated-IPv6-Pool’, ‘=’, ‘ipv6_drop’);

Eg: radgroupreply

12 INSERT INTO `radgroupreply` (`groupname`, `attribute`, `op`, `value`) VALUES(‘PLANO_BLOQUEADO’, ‘Mikrotik-Delegated-IPv6-Pool’, ‘=’, ‘ipv6_drop’);

This way, routerOS will interpret the pool name through the Mikrotik-Delegated-IPv6-Pool attribute, and as soon as the client logs in it will not take the valid block. You could even use a valid one and block it in the firewall (but that’s an amateur system thing #ficadica).

If your freradius is old (v2.x) you will need to have the dictionary updated.

I’ll leave an example of how to configure a pppoe client on a mikrotik, as I noticed a bug when it needs to renew its IPv6.

12345678910111213141516171819 /interface pppoe-clientadd add-default-route=yes disabled=no interface=ether1 name=pppoe-out1 password=SENHA use-peer-dns=yes user=SENHA /ipv6 dhcp-clientadd interface=pppoe-out1 pool-name=DHCPv6PD pool-prefix-length=56 request=prefix /ipv6 addressadd advertise=yes from-pool=DHCPv6PD interface=INTERFACE_LAN /ppp profileset *0 on-up=”{\r\    \n  /delay delay-time=5s\r\    \n  /ipv6 dhcp-client renew numbers=0\r\    \n  /delay delay-time=2s\r\    \n  /ipv6 dhcp-client renew numbers=0\r\    \n}”/ipv6 firewall mangleadd action=change-mss chain=forward new-mss=clamp-to-pmtu passthrough=yes \    protocol=tcp tcp-flags=syn

Little trick on the profile to renew the IP without a headache every time it authenticates (bugs)

123456 {  /delay delay-time=5s  /ipv6 dhcp-client renew numbers=0  /delay delay-time=2s  /ipv6 dhcp-client renew numbers=0}

Did you like it? I hope you are able to implement your IPv6 and are safe with the stored logs!

If you would like to make a donation to the cafe, I would be very happy for your recognition!

Leave a comment