High performance request remapping / rewriting
All, 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 = "00-11-22-33-44-55" Kind = "guest" # Fallback - unknown hosts DEFAULT Calling-Station-Id =~ "^[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}$" , 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: /etc/raddb/radiusd.conf: 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.
Phil Mayers <p.mayers@imperial.ac.uk> wrote:
...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.
The simplest way is to not use the "users" file. Use rlm_passwd, and have multiple instances. e.g. modules { ... passwd nas2zone { filename = ${raddbdir}/nas2zone.txt format = "*Client-IP-Address:~Zone-Name hashsize = 1000 allowmultiplekeys = no } passwd mac2type { filename = ${raddbdir}/mac2type.txt format = "*Calling-Station-Id:~MAC-Type hashsize = 1000 allowmultiplekeys = no } ... } List "nas2zone" and "mac2type" in the "authorize" section, and create new attribures in the dictionary: Zone-Name & MAC-Type. After that, the rlm_passwd module can't really use 2 attributes to look up data, so you'll have to use the "users" file, as in your post. But the "passwd" module lets you manage the nas & Mac mappings as simple flat-text files, which is pretty nice.
# Fallback - unknown hosts DEFAULT Calling-Station-Id =~ "^[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}-[0-9A-F]{2}$" , User-Password = `%{0}` Kind = "unreg"
The "passwd" module doesn't have defaults, so I'm not sure how to do the above.
Ideally an apache-like feature of DBM mapping is what's needed of something like:
/etc/raddb/radiusd.conf:
attr_rewrite nas2zone { attribute = NAS-IP-Address searchin = packet searchfor = "(.*)" replacewith = "%{dbm:nas2zone:%{1}}"
That's not a bad idea. The dynamic string expansion functionality of the server would be very good for that.
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.
That is a high load.
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:
No, sorry. That would be a good idea, though. OpenRADIUS does this, which is a good idea for many situations. But passing that much data through a socket may be problematic. If all else fails, try using rlm_perl. The version in 1.0.4 may have issues, but the one in the CVS head should be OK. Alan DeKok.
participants (2)
-
Alan DeKok -
Phil Mayers