Skip to content

Commit

Permalink
[change] Rough support for extending counters to support Gigawords
Browse files Browse the repository at this point in the history
  • Loading branch information
nemesifier committed Mar 27, 2024
1 parent 81181fa commit 69df3e7
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 50 deletions.
14 changes: 8 additions & 6 deletions openwisp_radius/api/freeradius_views.py
Expand Up @@ -304,14 +304,15 @@ def get_replies(self, user, organization_id):

group_checks = get_group_checks(user_group.group)

for counter in app_settings.COUNTERS:
group_check = group_checks.get(counter.check_name)
for Counter in app_settings.COUNTERS:
group_check = group_checks.get(Counter.check_name)
if not group_check:
continue
try:
remaining = counter(
counter = Counter(
user=user, group=user_group.group, group_check=group_check
).check()
)
remaining = counter.check()
except SkipCheck:
continue
# if max is reached send access rejected + reply message
Expand All @@ -325,14 +326,15 @@ def get_replies(self, user, organization_id):
continue
if remaining is None:
continue
reply_name = counter.reply_name
# send remaining value in RADIUS reply, if needed.
# This emulates the implementation of sqlcounter in freeradius
# which sends the reply message only if the value is smaller
# than what was defined to a previous reply message
if counter.reply_name not in data or remaining < self._get_reply_value(
if reply_name not in data or remaining < self._get_reply_value(
data, counter
):
data[counter.reply_name] = remaining
data[reply_name] = remaining

return data, self.accept_status

Expand Down
9 changes: 6 additions & 3 deletions openwisp_radius/api/serializers.py
Expand Up @@ -285,12 +285,15 @@ class Meta:

def get_result(self, obj):
try:
counter = app_settings.CHECK_ATTRIBUTE_COUNTERS_MAP[obj.attribute]
remaining = counter(
Counter = app_settings.CHECK_ATTRIBUTE_COUNTERS_MAP[obj.attribute]
counter = Counter(
user=self.context['user'],
group=self.context['group'],
group_check=obj,
).check()
)
# Python can handle 64 bit numbers and
# hence we don't need to display Gigawords
remaining = counter.check(gigawords=False)
return int(obj.value) - remaining
except MaxQuotaReached:
return int(obj.value)
Expand Down
3 changes: 2 additions & 1 deletion openwisp_radius/counters/base.py
Expand Up @@ -43,6 +43,7 @@ def get_sql_params(self, start_time, end_time): # pragma: no cover
# sqlcounter module, now we can translate it with gettext
# or customize it (in new counter classes) if needed
reply_message = _('Your maximum daily usage time has been reached')
gigawords = False

def __init__(self, user, group, group_check):
self.user = user
Expand Down Expand Up @@ -92,7 +93,7 @@ def get_counter(self):
# or if nothing is returned (no sessions present), return zero
return row[0] or 0

def check(self):
def check(self, gigawords=gigawords):
if not self.group_check:
raise SkipCheck(
message=(
Expand Down
128 changes: 96 additions & 32 deletions openwisp_radius/radclient/dictionary
Expand Up @@ -143,43 +143,107 @@ VALUE NAS-Port-Type Cable 17
VALUE NAS-Port-Type Wireless-Other 18
VALUE NAS-Port-Type Wireless-802.11 19

# -*- text -*-
# Copyright (C) 2019 The FreeRADIUS Server project and contributors
# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0
#
##############################################################################
#
# ChilliSpot (and CoovaChilli) captive portal
# http://www.chillispot.org
# http://coova.org/wiki/index.php/CoovaChilli
# CoovaChilli captive portal
# http://coova.github.io/
#
# $Id: c086a0790f2151b24f0d64c3e466eba18643891e $
#
##############################################################################

VENDOR ChilliSpot 14559

BEGIN-VENDOR ChilliSpot

ATTRIBUTE ChilliSpot-Max-Input-Octets 1 integer
ATTRIBUTE ChilliSpot-Max-Output-Octets 2 integer
ATTRIBUTE ChilliSpot-Max-Total-Octets 3 integer
ATTRIBUTE ChilliSpot-Bandwidth-Max-Up 4 integer
ATTRIBUTE ChilliSpot-Bandwidth-Max-Down 5 integer
ATTRIBUTE ChilliSpot-Config 6 string
ATTRIBUTE ChilliSpot-Lang 7 string
ATTRIBUTE ChilliSpot-Version 8 string
ATTRIBUTE ChilliSpot-OriginalURL 9 string

# Configuration management parameters (ChilliSpot Only)
ATTRIBUTE ChilliSpot-UAM-Allowed 100 string
ATTRIBUTE ChilliSpot-MAC-Allowed 101 string
ATTRIBUTE ChilliSpot-Interval 102 integer
VENDOR CoovaChilli 14559

BEGIN-VENDOR CoovaChilli

ATTRIBUTE CoovaChilli-Max-Input-Octets 1 integer
ATTRIBUTE CoovaChilli-Max-Output-Octets 2 integer
ATTRIBUTE CoovaChilli-Max-Total-Octets 3 integer
ATTRIBUTE CoovaChilli-Bandwidth-Max-Up 4 integer
ATTRIBUTE CoovaChilli-Bandwidth-Max-Down 5 integer
ATTRIBUTE CoovaChilli-Config 6 string
ATTRIBUTE CoovaChilli-Lang 7 string
ATTRIBUTE CoovaChilli-Version 8 string
ATTRIBUTE CoovaChilli-OriginalURL 9 string
ATTRIBUTE CoovaChilli-Acct-View-Point 10 integer

VALUE CoovaChilli-Acct-View-Point CoovaChilli-NAS-View-Point 1
VALUE CoovaChilli-Acct-View-Point CoovaChilli-Client-View-Point 2

ATTRIBUTE CoovaChilli-Require-UAM 11 string
ATTRIBUTE CoovaChilli-Require-Splash 12 string
ATTRIBUTE CoovaChilli-Route-To-Interface 13 string
ATTRIBUTE CoovaChilli-Config-File 14 string

ATTRIBUTE CoovaChilli-Session-State 15 integer

VALUE CoovaChilli-Session-State Authorized 1
VALUE CoovaChilli-Session-State NotAuthorized 2
VALUE CoovaChilli-Session-State Started 3
VALUE CoovaChilli-Session-State Stopped 4
VALUE CoovaChilli-Session-State UserLogoutUrl 10
VALUE CoovaChilli-Session-State IdleTimeoutReached 11
VALUE CoovaChilli-Session-State TimeoutReached 12
VALUE CoovaChilli-Session-State LogoutTimeReached 13
VALUE CoovaChilli-Session-State InDataLimitReached 14
VALUE CoovaChilli-Session-State OutDataLimitReached 15
VALUE CoovaChilli-Session-State TotalDataLimitReached 16
VALUE CoovaChilli-Session-State LocationChanged 17

ATTRIBUTE CoovaChilli-Session-Id 16 string
ATTRIBUTE CoovaChilli-AP-Session-Id 17 string
ATTRIBUTE CoovaChilli-User-Agent 18 string
ATTRIBUTE CoovaChilli-Accept-Language 19 string

ATTRIBUTE CoovaChilli-Max-Input-Gigawords 21 integer
ATTRIBUTE CoovaChilli-Max-Output-Gigawords 22 integer
ATTRIBUTE CoovaChilli-Max-Total-Gigawords 23 integer

ATTRIBUTE CoovaChilli-VLAN-Id 24 integer
ATTRIBUTE CoovaChilli-Location 25 string
ATTRIBUTE CoovaChilli-Old-Location 26 string
ATTRIBUTE CoovaChilli-Location-Change-Count 27 integer

ATTRIBUTE CoovaChilli-Sys-Uptime 40 integer
ATTRIBUTE CoovaChilli-Sys-LoadAvg 41 string
ATTRIBUTE CoovaChilli-Sys-Memory 42 string

ATTRIBUTE CoovaChilli-DHCP-Vendor-Class-Id 50 octets
ATTRIBUTE CoovaChilli-DHCP-Client-Id 51 octets
ATTRIBUTE CoovaChilli-DHCP-Options 52 octets
ATTRIBUTE CoovaChilli-DHCP-Filename 53 string
ATTRIBUTE CoovaChilli-DHCP-Hostname 54 string
ATTRIBUTE CoovaChilli-DHCP-Server-Name 55 string
ATTRIBUTE CoovaChilli-DHCP-Client-FQDN 56 string
ATTRIBUTE CoovaChilli-DHCP-Parameter-Request-List 57 octets

ATTRIBUTE CoovaChilli-DHCP-IP-Address 60 ipaddr
ATTRIBUTE CoovaChilli-DHCP-Netmask 61 ipaddr
ATTRIBUTE CoovaChilli-DHCP-DNS1 62 ipaddr
ATTRIBUTE CoovaChilli-DHCP-DNS2 63 ipaddr
ATTRIBUTE CoovaChilli-DHCP-Gateway 64 ipaddr
ATTRIBUTE CoovaChilli-DHCP-Domain 65 string
ATTRIBUTE CoovaChilli-DHCP-Relay 66 ipaddr

ATTRIBUTE CoovaChilli-Inject-URL 70 string

ATTRIBUTE CoovaChilli-PostAuthProxy-Address 75 ipaddr
ATTRIBUTE CoovaChilli-PostAuthProxy-Port 76 integer

ATTRIBUTE CoovaChilli-Garden-Input-Octets 80 integer
ATTRIBUTE CoovaChilli-Garden-Output-Octets 81 integer
ATTRIBUTE CoovaChilli-Garden-Input-Gigawords 82 integer
ATTRIBUTE CoovaChilli-Garden-Output-Gigawords 83 integer
ATTRIBUTE CoovaChilli-Other-Input-Octets 84 integer
ATTRIBUTE CoovaChilli-Other-Output-Octets 85 integer
ATTRIBUTE CoovaChilli-Other-Input-Gigawords 86 integer
ATTRIBUTE CoovaChilli-Other-Output-Gigawords 87 integer

# Configuration management parameters (CoovaChilli Only)
ATTRIBUTE CoovaChilli-UAM-Allowed 100 string
ATTRIBUTE CoovaChilli-MAC-Allowed 101 string
ATTRIBUTE CoovaChilli-Interval 102 integer

# Inline with RFC 2882 use of VSE-Authorize-Only for remote config
# Note that 14559 = 0x38df is used as prefix for the VSE.
# This is recognized as the best (but bad) way of doing VSEs.
# (ChilliSpot Only - CoovaChilli uses Service-Type = Administrative-User)
VALUE Service-Type ChilliSpot-Authorize-Only 0x38df0001
# (CoovaChilli Only - CoovaChilli uses Service-Type = Administrative-User)
VALUE Service-Type CoovaChilli-Authorize-Only 0x38df0001

END-VENDOR ChilliSpot
END-VENDOR CoovaChilli
24 changes: 16 additions & 8 deletions openwisp_radius/tasks.py
Expand Up @@ -12,6 +12,7 @@
from django.utils import timezone, translation
from django.utils.translation import gettext_lazy as _

from openwisp_radius.utils import get_group_checks, get_user_group
from openwisp_utils.admin_theme.email import send_email
from openwisp_utils.tasks import OpenwispCeleryTask

Expand Down Expand Up @@ -141,18 +142,24 @@ def get_radsecret_from_radacct(rad_acct):
f'Failed to parse NAS IP network for "{nas.id}" object. Skipping!'
)

def get_radius_reply_name(check):
def get_radius_reply_name_and_value(user, check):
Counter = app_settings.CHECK_ATTRIBUTE_COUNTERS_MAP[check.attribute]
counter = Counter(user=user, group=check.group, group_check=check)
try:
return app_settings.CHECK_ATTRIBUTE_COUNTERS_MAP[check.attribute].reply_name
value = counter.check()
return counter.reply_name, value
except KeyError:
return check.attribute
return check.attribute, check.value
except Exception as e:
logger.exception(f'Got {e} while CoA for counter {Counter}')

def get_radius_attributes():
def get_radius_attributes(user):
attributes = {}
rad_group_checks = RadiusGroupCheck.objects.filter(group_id=new_group_id)
if rad_group_checks:
for check in rad_group_checks:
attributes[get_radius_reply_name(check)] = f'{check.value}'
reply_name, value = get_radius_reply_name_and_value(user, check)
attributes[reply_name] = f'{value}'
elif (
not rad_group_checks
and RadiusGroup.objects.filter(id=new_group_id).exists()
Expand All @@ -161,7 +168,8 @@ def get_radius_attributes():
# Unset attributes set by the previous group.
rad_group_checks = RadiusGroupCheck.objects.filter(group_id=old_group_id)
for check in rad_group_checks:
attributes[get_radius_reply_name(check)] = ''
reply_name, _ = get_radius_reply_name_and_value(user, check)
attributes[reply_name] = ''
return attributes

try:
Expand All @@ -171,7 +179,7 @@ def get_radius_attributes():
f'Failed to find user with "{user_id}" ID. Skipping CoA operation.'
)
return
# Check is user has open RadiusAccounting sessions
# Check if user has open RadiusAccounting sessions
open_sessions = RadiusAccounting.objects.filter(
username=user.username, stop_time__isnull=True
).select_related('organization', 'organization__radius_settings')
Expand All @@ -190,7 +198,7 @@ def get_radius_attributes():
)
return
else:
attributes = get_radius_attributes()
attributes = get_radius_attributes(user)

attributes['User-Name'] = user.username
updated_sessions = []
Expand Down

0 comments on commit 69df3e7

Please sign in to comment.