Skip to content

Instantly share code, notes, and snippets.

@nasirhafeez
Last active July 24, 2025 10:18
Show Gist options
  • Save nasirhafeez/6669b24aab0bda545f60f9da5ed14f25 to your computer and use it in GitHub Desktop.
Save nasirhafeez/6669b24aab0bda545f60f9da5ed14f25 to your computer and use it in GitHub Desktop.
FreeRADIUS Advanced Use Cases

FreeRADIUS Advanced Use Cases

Contents

Introduction

Expiration

Changing IP Pool for Expired Username

Expiration After Certain Connection Time

Wrong Password Notification

User MAC Binding

User Binding with NAS

Restrict Service Type

SQLCounter

User Disconnection from RADIUS

Introduction

This document presents the configurations related to some advanced FreeRADIUS use cases.

Expiration

The Expiration attribute is entered in radcheck table as follows:

image2

This means that this username will expire on 25 Apr 2020 at 10:42:00. Session-Timeout is given to user based on this.

Note: expiration keyword needs to be present in authorize section in /etc/freeradius/3.0/sites-enabled/default (it is present by default in FreeRADIUS 3.x).

Changing IP Pool for Expired Username

Add the following code in authorize section of /etc/freeradius/3.0/sites-enabled/default:

expiration{
	userlock = 1
}
if(userlock){
	# Let him connect with EXPIRED pool in reply
	ok
	update reply {
	Reply-Message := "Your account has expired, %{User-Name} / Reason: DATE LIMIT REACHED"
	Framed-Pool := "Expired"
	}
}

Testing results:

image3

image4

Expiration After Certain Connection Time

Suppose a user has paid for a certain amount of time and their username needs to be expired after that much time online. We can use the Expire-After attribute for that:

image5

Wrong Password Notification

Enter the following code in Post-Auth-Type REJECT section of /etc/freeradius/3.0/sites-enabled/default:

update reply {
	Reply-Message = 'Wrong Password'
}

image6

User MAC Binding

A new table is created to store username to MAC address bindings:

CREATE TABLE IF NOT EXISTS `macs` (
	`id` int unsigned NOT NULL AUTO_INCREMENT,
	`username` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
	`callingstationid` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'na',
	PRIMARY KEY (`id`)
);

The following code is added to authorize section for MAC addition and authorization. When a username is initially created it has the MAC address set to na. Upon first connection the MAC address is saved in macs table, and subsequently it is checked.

update control{
	#check if callingstationid does not exist in mac_limit Table
	Tmp-String-0 = "%{sql: SELECT callingstationid FROM macs WHERE username = '%{User-Name}'}"

	#check if mac is available or not in mac_limit table
	Tmp-Integer-2 = "%{sql: SELECT count(*) FROM macs WHERE username = '%{User-Name}' AND callingstationid='%{Calling-Station-Id}'}"
}

if(&control:Tmp-String-0 == "na"){
	"%{sql: UPDATE macs SET callingstationid = '%{Calling-Station-Id}' WHERE Username = '%{User-Name}'}"
	update reply {
		Reply-Message := "New mac Added"
	}
}

elsif(&control:Tmp-Integer-2 == 0){
	update reply {
		Reply-Message += "MAC auth Failed"
	}
	reject
}

User Binding with NAS

A new table is created to store huntgroups:

CREATE TABLE radhuntgroup (
    id int(11) unsigned NOT NULL auto_increment,
    groupname varchar(64) NOT NULL default '',
    nasipaddress varchar(15) NOT NULL default '',
    nasportid varchar(15) default NULL,
    PRIMARY KEY  (id),
    KEY nasipaddress (nasipaddress)
) ;

NAS IP addresses are assigned to various huntgroups:

image7

Users are assigned to usergroups:

image8

The following code is added in authorize section after preprocess. This code adds the Huntgroup-Name attribute to RADIUS Access Request. Then, it allows users of usergroup hg1users to only be connected on NASs belonging to hg1. Similarly, users of usergroup usergroup2 are only allowed to be connected to NASs belonging to hg2.

update request{
	Huntgroup-Name := "%{sql:SELECT groupname FROM radhuntgroup WHERE nasipaddress='%{NAS-IP-Address}'}"
}

# bind hg1users usergroup to hg1 huntgroup
if (SQL-Group == "hg1users") {
	if (Huntgroup-Name == "hg1") {
		#ok
	}
	else {
		update reply {
			Reply-Message += "Error: User not allowed connection on this device"
		}		
		reject
	}
}
# bind hg2users usergroup to hg2 huntgroup
elsif (SQL-Group == "hg2users") {
	if (Huntgroup-Name == "hg2") {
		#ok
	}
	else {
		update reply {
			Reply-Message += "Error: User not allowed connection on this device"
		}	
		reject
	}
}

Reference

Restrict Service Type

Suppose we only want to allow requests to RADIUS of the following Service-Type:

  1. Framed
  2. Login

And we want to reject all other requests. The following code should be added to authorize section:

#Only allow service types "Framed-User" or "Login-User"; reject all others

if((&request:Service-Type=="Framed-User") || (&request:Service-Type=="Login-User")){
	#ok
}
else{
	update reply {
		Reply-Message += "Wrong service type"
	}
	reject
}

SQLCounter

Time Based Quota

We can use Session-Timeout attribute to limit session time of a user. For example, if we wanted to limit a user to only 30 minutes of network access daily we could set Session-Timeout to 1800 (1800 s = 30 min). This will ensure that the user automatically gets disconnected after 30 min. But the problem with this approach is that the user can connect again to get 30 more minutes. To solve this problem, FreeRADIUS has some pre-defined counters that can be used to assign time-based session limits (like daily, monthly, etc).

The file mods-enabled/sqlcounter contains several default counters. This is the configuration of daily counter:

sqlcounter dailycounter {
        sql_module_instance = sql
        dialect = mysql

        counter_name = Daily-Session-Time
        check_name = Max-Daily-Session
        reply_name = Session-Timeout

        key = User-Name
        reset = daily

        $INCLUDE ${modconfdir}/sql/counter/${dialect}/${.:instance}.conf
}

This counter checks for an internal attribute Max-Daily-Session and uses the session data in DB to calculate the remaining session time of the user for that day. It then adds that remaining Session-Timeout to RADIUS Access Accept packet.

We need to add dailycounter to sites-available/default in authorize section. In DB add the Max-Daily-Session attribute to radcheck table:

image9

Enable the sqlcounter module:

cd /etc/freeradius/3.0/mods-enabled
ln -s ../mods-available/sqlcounter

Volume Based Quota

We can use sqlcounter module to query accounting data in DB and impose volume-based limits. Suppose we want to allow a user to have 25 MB of volume daily.

In the file mods-enabled/sqlcounter create a new counter:

sqlcounter total_volume {

        sql_module_instance = sql
        dialect = sql

        counter_name = Max-Total-Volume
        check_name = Max-Volume

        key = User-Name
        reset = daily

        query = "SELECT SUM(acctinputoctets) + SUM(acctoutputoctets) FROM radacct WHERE UserName='%{${key}}'"
}

Register the counter total\_volume in authorize section in sites-available/default. Add the Max-Volume attribute for that user in bytes in radcheck table:

image10

Once the user has exceeded their volume limit they will not be authorized on their next connection attempt. Please note that users who are already connected will not be affected by this restriction. To perform any action on such already-connected sessions that have gone over their volume limit some script can be written that periodically queries the DB and if it finds some user has used up their data limit it performs some action on them (like disconnection, rate-limiting etc).

User Disconnection from RADIUS

To disconnect a user from RADIUS we use the user’s username and IP address and secret of NAS in radclient to disconnect that user:

root@ubuntu:~# echo 'User-Name = test1' | radclient -x 192.168.100.35:3799 disconnect ''1234''
Sent Disconnect-Request Id 28 from 0.0.0.0:35685 to 192.168.100.35:3799 length 27
	User-Name = "test1"
Received Disconnect-ACK Id 28 from 192.168.100.35:3799 to 0.0.0.0:0 length 30
	NAS-Identifier = "MikroTik"

Enabling incoming messages in Mikrotik NAS

image11

@V1rusFalcon
Copy link

hii, there is a problem with the daily total volume. once the limit is reached it resets

@nasirhafeez
Copy link
Author

nasirhafeez commented May 15, 2022

In my example the volume is reset on a daily basis (reset = daily). You can update this based on your requirements to weekly, monthly etc. It can also be 'never'. Check this out for further details.

@V1rusFalcon
Copy link

Sorry, i didn't gave enough information. Once the user reach the limit, input and output octets are reset to a random number that is lover than the limit, and the user can use internet again, all in the same day

@nasirhafeez
Copy link
Author

I haven't seen this issue.

@AhmadSudirman7
Copy link

AhmadSudirman7 commented Aug 31, 2022

hi can we set reset = 30 minutes mean I want to reset every 30 minutes

@nasirhafeez
Copy link
Author

Yes we can. The default counters like daily, monthly are predefined in FreeRADIUS. You can define a custom counter and do all the necessary configs to make it work. Check out the counters in /etc/freeradius/3.0/mods-config/sql/counter/mysql for example.

@AhmadSudirman7
Copy link

how to set ?
because i not see minutcounter.conf

@AhmadSudirman7
Copy link

sqlcounter hourlycounter {
sql_module_instance = sql
dialect = mysql

    counter_name = hourly-Session-Time
    check_name = Max-hourly-Session
    reply_name = Session-Timeout

    key = User-Name
    reset = 1hour

    $INCLUDE ${modconfdir}/sql/counter/${dialect}/${.:instance}.conf

}

i use this and this is working but dont know how to set 30 minutes can help how to solved or any ide for this case ?

@gvt2015
Copy link

gvt2015 commented Jan 25, 2023

Hi, how to set one time usage for user with 1gb limit and 1 day validity in free radius with SQL . After limit either any one condition reaches radius will ask for user credentials again Nas is tplink eap225. With External radius authentication

@nasirhafeez
Copy link
Author

Hi, how to set one time usage for user with 1gb limit and 1 day validity in free radius with SQL . After limit either any one condition reaches radius will ask for user credentials again Nas is tplink eap225. With External radius authentication

Use SQL counter with time and volume based quotas

@hidasw
Copy link

hidasw commented Aug 23, 2023

sqlcounter hourlycounter { sql_module_instance = sql dialect = mysql

    counter_name = hourly-Session-Time
    check_name = Max-hourly-Session
    reply_name = Session-Timeout

    key = User-Name
    reset = 1hour

    $INCLUDE ${modconfdir}/sql/counter/${dialect}/${.:instance}.conf

}

i use this and this is working but dont know how to set 30 minutes can help how to solved or any ide for this case ?

1h is minimum. i tried to set 0.1h but in sqllog.sql still 1 hour (timestamp) interval. please coba saja.

@hamzasec
Copy link

there is no rest api for this like create user on radius server and set daily access??

@nasirhafeez
Copy link
Author

nasirhafeez commented Oct 19, 2023 via email

@hamzasec
Copy link

openwisp better than daloradius ??
my use case is to create co-working space and each user need to use ethernet or wifi should buy it daily or weekly after the time is finished she could not connect with that user anymore

@nasirhafeez
Copy link
Author

nasirhafeez commented Oct 19, 2023 via email

@hamzasec
Copy link

But it's diffucult that each new one come to create new user on radius and set password and expiration date like your tutorial tell is to add this information each time on the database
https://gist.github.com/nasirhafeez/6669b24aab0bda545f60f9da5ed14f25#expiration

@nasirhafeez
Copy link
Author

nasirhafeez commented Oct 19, 2023 via email

@hamzasec
Copy link

I have the front end part it is mobile application developed with flutter i need the back-end one to integrate with it so i search for rest api that already created to implement on my application

@JumaBryan
Copy link

I have the front end part it is mobile application developed with flutter i need the back-end one to integrate with it so i search for rest api that already created to implement on my application

Hello, I could help you setup the back-end api

@welangaieric
Copy link

how can i get session timeout that is stored in radgroupcheck for hotspot users

@Pavewleln
Copy link

If I add a link to
cd /etc/freeradius/3.0/ files with
ln -s ../mods-available/sqlcounte mods
, I will get this view
Checking the configuration of the FreeRADIUS daemon...error (duplicate module "sqlcounter dailycounter { ... }", in the file /etc/freeradius/mods-available/sqlcounter:1 and the file /etc/freeradius/mods-enabled/sqlcounter:1).

How can I fix this?

@Toe-Hlaing-Win
Copy link

Toe-Hlaing-Win commented Mar 25, 2024

Dear,

Is there any way to record hourly usage data.

I try with session-timeout : 3600 seconds and Acct-Interim-Interval : 600 seconds.

It update every 10 mintute and session is stop every 1 hour.

so i got, radacct records for hourly usage.

But, user is disconnect every hours.

@nasirhafeez
Copy link
Author

Don't use session-timeout. Simply query radacct for last hour's records based on interim updates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment