High performance request remapping / rewriting

Phil Mayers p.mayers at imperial.ac.uk
Fri Jul 8 15:15:09 CEST 2005


We are looking to implement mac-based vlans with a radius backend. I'm 
hoping freeradius is the obvious choice, but I'm having a hard time 
seeing how to do what I need.

What I'm looking to is feed FreeRadius from our host registration 
database. Each NAS (switch) may potentially have different VLANs on it, 
and each registered host may fall into a different vlan "type", so the 
radius server needs to map:

(clientmac, nasipaddress) -> vlantag

However, there are ~20k MAC addresses and ~1200 NASes (switches), so 
clearly I can't do this:

DEFAULT Calling-Station-Id = "<mac0>", NAS-IP-Address = "<switch0>"
DEFAULT Calling-Station-Id = "<mac0>", NAS-IP-Address = "<switch1>"
DEFAULT Calling-Station-Id = "<mac0>", NAS-IP-Address = "<switch2>"
DEFAULT Calling-Station-Id = "<mac20k>", NAS-IP-Address = "<switch1k>"

...because it's 20 million entries. The file as a users compiled to dbm 
is >4Gb :o(

I wanted to do some kind of optimisation - the switches are grouped into 
zones in our database, and vlans are specific to these zones (normally a 
building), so actually something like:

nasip -> zone
(clientmac, zone) -> vlan

...but that's still too large, so maybe:

nasip -> zone
clientmac -> clienttype (there are 5 types - unreg, guest, roaming, 
home, blocked)
(clienttype, zone) -> vlan

...which would be much smaller, but I can't see how you do this. I must 
admit to being somewhat confused about the request, check and reply 
items, but from what I can tell a "users" item consists of:

username OR DEFAULT <space> [comma-separated items to check against 
request] <newline>
   [comma-separated items to add to reply]

...so even with Fall-Through=Yes you can never do this:

DEFAULT NAS-IP-Address = blah
   Zone = "foo"

DEFAULT Calling-Station-Id = "00-11-22-33-44-55", User-Password = 
   Kind = "guest"

# Fallback - unknown hosts
DEFAULT Calling-Station-Id =~ 
, User-Password = `%{0}`
   Kind = "unreg"

DEFAULT Zone = "foo", Kind = "unreg"
   Tunnel-Type = VLAN,
   Tunnel-Medium-Type = IEEE-802,
   Tunnel-Private-Group-ID := "1"

DEFAULT Zone = "foo", Kind = "guest"
   Tunnel-Type = VLAN,
   Tunnel-Medium-Type = IEEE-802,
   Tunnel-Private-Group-ID := "2"

...because Zone and Kind are set in the reply, so can't be matched 
further down.

rlm_attr_rewrite has the beginnings of what is needed, but a linear 
search through 20k regexps for the hosts, followed by 1k regexps for the 
switches clearly isn't going to work.

Ideally an apache-like feature of DBM mapping is what's needed of 
something like:


attr_rewrite nas2zone {
   attribute = NAS-IP-Address
   searchin = packet
   searchfor = "(.*)"
   replacewith = "%{dbm:nas2zone:%{1}}"
   new_attribute_name = "Zone"

...and similarly for Calling-Station-Id -> kind

The issue is that this needs to go very very fast - at peak times (e.g. 
say a reboot of a PC cluster during overnight maintenance) the DHCP 
servers get ~50 requests/second, so a radius server(s) would need to 
answer with similar performance.

I originally tried to do this with rlm_sql direct to our registration 
database, but the performance was abominal (which is not an SQL issue) 
and eventually it hung the radius server anyway (rlm_sql_postgresql). In 
any event I was never super-keen on that for security reasons, though 
the fact it was instant-updating once a registration was processed was 
very handy.

I'm assuming rlm_exec would have similar if not worse performance 
characterisitcs (spawning 50 processes a second during peak times does 
not strike me as overly sensible). Is there an rlm_socket:

socket mac_vlan {
   path = "/var/run/mac_vlan.unixsock"
   wait = yes
   input_pairs = request
   output_pairs = reply

...i.e. keep a persistent connection to something open.

I'd appreciate any suggestions.

More information about the Freeradius-Users mailing list