Jade's Tech Notes

More bits more places

Mikrotik DHCP & FreeRADIUS With a Hint of Unlang

Mikrotik has two particularly useful subscriber management features built into the DHCP server on RouterOS. First, If you set the rate-limit property on a DHCP lease it will dynamically manage a Simple Queue to enforce the rate limit. Second, it will authenticate to a RADIUS server using the DHCP Client’s MAC address as the username. The RADIUS server can reply with the IP address, pool, or traffic shaping paramaters.

We use this combination to rate limit customers on Ubiquiti AirMax equipment since Ubiquiti is somewhat unaccommodating towards OSS/BSS integrations. When we set up our system we envisioned three states of a customer so far as DHCP RADIUS is concerned:

  1. Users that aren’t known to the system don’t get a IP address. We do this by setting the default address-pool on the DHCP server instance to static-only.
  2. A user that is known to the system and has a active account is assigned a DHCP Pool of ubnt-cust (for Ubiquiti customer).
  3. A user with a delinquent account is assigned to the ubnt-deact (for Ubiquiti deactivated) pool. Users in this pool are assigned a RFC1918 IP address and redirected to a splash page.

You might think that we would want unknown users to get a splash page, and we do on our Cambium Canopy network which comes with strong AAA support. On the Ubiquiti network we assume that anyone we don’t know about that manages to connect to our network is hostile and want to give them as little data as possible. Every little obstacle helps. I may relax this after exploring the fairly new RADIUS authentication support in Ubiquiti AirOS.

Anyway, We ended up with a Mikrotik router at each AirMax tower running RADIUS-backed DHCP. Our B/OSS provisions our existing FreeRADIUS system with Ubiquiti device MAC addresses as usernames and puts them in a group with a Mikrotik-Rate-Limit attribute that matches the customer’s speed package. Everything is working great until…

We started to retrofit existing towers with AirMax. These towers were often connected in a star topology, as opposed to our previous expansion where we build out in a line and then expand to the side to make a box. Since we had a central tower it didn’t make sense to deploy a router at every spoke site1. Of course each tower (at a minimum) has its own VLAN to make management easier and contain bad behavior. This presented a problem since the Mikrotik DHCP server can’t dynamically select the correct subnet based on the ingress interface (unlike the Cisco IOS or ISC DHCPd servers). The solution is to create a separately named pool for each interface/tower and get the RADIUS server to provide the appropriate pool in the RADIUS reply. How do we do that? Well, the “easy” way would be to manually set a different pool for each tower in RADIUS. The problem is that is more work on a ongoing basis.

What to do? Well, the Mikrotik DHCP server sends the DHCP server name in the RADIUS request as the Called-Station-ID. On all our towers we had been setting this to ubnt-server. On routers serving multiple towers I set the server name to ubnt-server-TWRID2. Then I setup the pools for each tower as ubnt-cust-TWRID and ubnt-deact-TWRID. In the FreeRADIUS server config I used unlang to extract the TWRID portion of each server name in the request and append it to the pool in the reply.

in sites-enabled/default:

post-auth {
 # Rewrite Mikrotik IP Pool assignments for routers with multiple pools
 if (request:Called-Station-Id =~ /^ubnt-server-(.*$)/) {
  update reply {
   Framed-Pool := "%{reply:Framed-Pool}-%{1}"

Example Mikrotik DHCP server config:

/ip pool
add name=ubnt-cust-HRT2 ranges=199.X.X.2-199.X.X.14
add name=ubnt-deact-HRT2 ranges=
add name=ubnt-deact-HRT1 ranges=
add name=ubnt-deact-SLNG1 ranges=
add name=ubnt-cust-HRT1 ranges=199.X.X.18-199.X.X.30
add name=ubnt-cust-SLNG1 ranges=199.X.X.34-199.X.X.46

/ip dhcp-server
add add-arp=yes authoritative=yes disabled=no interface=v100-ether7 \
    lease-time=30m name= ubnt-server-HRT2 src-address=199.X.X.1 use-radius=yes
add add-arp=yes authoritative=yes disabled=no interface=v100-ether8 \
    lease-time=30m name=ubnt-server-HRT1 src-address=199.X.X.17 use-radius=yes
add add-arp=yes authoritative=yes disabled=no interface=v201-ether9 \
    lease-time=30m name=ubnt-server-SLNG1 src-address=199.X.X.33 use-radius=yes

[1] Since you aren’t doing subscriber management at the tower you need some kind of rate limiting in the CPE as a safety net to prevent subscriber-originated UDP floods saturating the tower backhaul on their way to the router.

[2] TWRID is the unique ID for each tower. We base it off of psudo-CLLI code. SLNG1, HRFD2, WHWR1, etc.