Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
O
OpnSense
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kulya
OpnSense
Commits
d9d68b45
Commit
d9d68b45
authored
Oct 14, 2015
by
Ad Schellevis
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
(captiveportal, new) cleanup and add features to background process
parent
caf43295
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
160 additions
and
71 deletions
+160
-71
cp-background-process.py
...e/scripts/OPNsense/CaptivePortal/cp-background-process.py
+160
-71
No files found.
src/opnsense/scripts/OPNsense/CaptivePortal/cp-background-process.py
View file @
d9d68b45
...
@@ -39,29 +39,76 @@ from lib.arp import ARP
...
@@ -39,29 +39,76 @@ from lib.arp import ARP
from
lib.ipfw
import
IPFW
from
lib.ipfw
import
IPFW
from
lib.daemonize
import
Daemonize
from
lib.daemonize
import
Daemonize
def
main
():
class
CPBackgroundProcess
(
object
):
""" background process helper class
"""
def
__init__
(
self
):
# open syslog and notice startup
syslog
.
openlog
(
'captiveportal'
,
logoption
=
syslog
.
LOG_DAEMON
)
syslog
.
openlog
(
'captiveportal'
,
logoption
=
syslog
.
LOG_DAEMON
)
syslog
.
syslog
(
syslog
.
LOG_NOTICE
,
'starting captiveportal background process'
)
syslog
.
syslog
(
syslog
.
LOG_NOTICE
,
'starting captiveportal background process'
)
# handle to ipfw, arp and the config
# handles to ipfw, arp the config and the internal administration
ipfw
=
IPFW
()
self
.
ipfw
=
IPFW
()
arp
=
ARP
()
self
.
arp
=
ARP
()
cnf
=
Config
()
self
.
cnf
=
Config
()
while
True
:
self
.
db
=
DB
()
try
:
self
.
_conf_zone_info
=
self
.
cnf
.
get_zones
()
# construct objects
db
=
DB
()
def
list_zone_ids
(
self
):
""" return zone numbers
# update accounting info
"""
db
.
update_accounting_info
(
ipfw
.
list_accounting_info
())
return
self
.
_conf_zone_info
.
keys
()
# process sessions per zone
def
initialize_fixed
(
self
):
arp
.
reload
()
""" initialize fixed ip / hosts per zone
cpzones
=
cnf
.
get_zones
()
"""
cpzones
=
self
.
_conf_zone_info
for
zoneid
in
cpzones
:
for
zoneid
in
cpzones
:
registered_addresses
=
ipfw
.
list_table
(
zoneid
)
for
conf_section
in
[
'allowedaddresses'
,
'allowedmacaddresses'
]:
registered_add_accounting
=
ipfw
.
list_accounting_info
()
for
address
in
cpzones
[
zoneid
][
conf_section
]:
expected_clients
=
db
.
list_clients
(
zoneid
)
if
conf_section
.
find
(
'mac'
)
==
-
1
:
concurrent_users
=
db
.
find_concurrent_user_sessions
(
zoneid
)
sessions
=
self
.
db
.
sessions_per_address
(
zoneid
,
ip_address
=
address
)
ip_address
=
address
mac_address
=
None
else
:
sessions
=
self
.
db
.
sessions_per_address
(
zoneid
,
mac_address
=
address
)
ip_address
=
None
mac_address
=
address
sessions_deleted
=
0
for
session
in
sessions
:
if
session
[
'authenticated_via'
]
not
in
(
'---ip---'
,
'---mac---'
):
sessions_deleted
+=
1
self
.
db
.
del_client
(
zoneid
,
session
[
'sessionId'
])
if
sessions_deleted
==
len
(
sessions
)
or
len
(
sessions
)
==
0
:
# when there's no session active, add a new one
# (only administrative, the sync process will add it if neccesary)
if
ip_address
is
not
None
:
self
.
db
.
add_client
(
zoneid
,
"---ip---"
,
""
,
ip_address
,
""
)
else
:
self
.
db
.
add_client
(
zoneid
,
"---mac---"
,
""
,
""
,
mac_address
)
# cleanup removed static sessions
for
dbclient
in
self
.
db
.
list_clients
(
zoneid
):
if
dbclient
[
'authenticated_via'
]
==
'---ip---'
\
and
dbclient
[
'ipAddress'
]
not
in
cpzones
[
zoneid
][
'allowedaddresses'
]:
self
.
ipfw
.
delete
(
zoneid
,
dbclient
[
'ipAddress'
])
self
.
db
.
del_client
(
zoneid
,
dbclient
[
'sessionId'
])
elif
dbclient
[
'authenticated_via'
]
==
'---mac---'
\
and
dbclient
[
'macAddress'
]
not
in
cpzones
[
zoneid
][
'allowedmacaddresses'
]:
if
dbclient
[
'ipAddress'
]
!=
''
:
self
.
ipfw
.
delete
(
zoneid
,
dbclient
[
'ipAddress'
])
self
.
db
.
del_client
(
zoneid
,
dbclient
[
'sessionId'
])
def
sync_zone
(
self
,
zoneid
):
""" Synchronize captiveportal zone.
Handles timeouts and administrative changes to this zones sessions
"""
if
zoneid
in
self
.
_conf_zone_info
:
# fetch data for this zone
cpzone_info
=
self
.
_conf_zone_info
[
zoneid
]
registered_addresses
=
self
.
ipfw
.
list_table
(
zoneid
)
registered_addr_accounting
=
self
.
ipfw
.
list_accounting_info
()
expected_clients
=
self
.
db
.
list_clients
(
zoneid
)
concurrent_users
=
self
.
db
.
find_concurrent_user_sessions
(
zoneid
)
# handle connected clients, timeouts, address changes, etc.
# handle connected clients, timeouts, address changes, etc.
for
db_client
in
expected_clients
:
for
db_client
in
expected_clients
:
...
@@ -73,49 +120,91 @@ def main():
...
@@ -73,49 +120,91 @@ def main():
drop_session_reason
=
None
drop_session_reason
=
None
# session cleanups, only for users not for static hosts/ranges.
# session cleanups, only for users not for static hosts/ranges.
if
db_client
[
'userName'
]
!=
''
:
if
db_client
[
'authenticated_via'
]
not
in
(
'---ip---'
,
'---mac---'
)
:
# check if hardtimeout is set and overrun for this session
# check if hardtimeout is set and overrun for this session
if
'hardtimeout'
in
cpzones
[
zoneid
]
and
str
(
cpzones
[
zoneid
]
[
'hardtimeout'
])
.
isdigit
():
if
'hardtimeout'
in
cpzone_info
and
str
(
cpzone_info
[
'hardtimeout'
])
.
isdigit
():
# hardtimeout should be set and we should have collected some session data from the client
# hardtimeout should be set and we should have collected some session data from the client
if
int
(
cpzones
[
zoneid
]
[
'hardtimeout'
])
>
0
and
float
(
db_client
[
'startTime'
])
>
0
:
if
int
(
cpzone_info
[
'hardtimeout'
])
>
0
and
float
(
db_client
[
'startTime'
])
>
0
:
if
(
time
.
time
()
-
float
(
db_client
[
'startTime'
]))
/
60
>
int
(
cpzones
[
zoneid
]
[
'hardtimeout'
]):
if
(
time
.
time
()
-
float
(
db_client
[
'startTime'
]))
/
60
>
int
(
cpzone_info
[
'hardtimeout'
]):
drop_session_reason
=
"session
%
s hit hardtimeout"
%
db_client
[
'sessionId'
]
drop_session_reason
=
"session
%
s hit hardtimeout"
%
db_client
[
'sessionId'
]
# check if idletimeout is set and overrun for this session
# check if idletimeout is set and overrun for this session
if
'idletimeout'
in
cpzones
[
zoneid
]
and
str
(
cpzones
[
zoneid
]
[
'idletimeout'
])
.
isdigit
():
if
'idletimeout'
in
cpzone_info
and
str
(
cpzone_info
[
'idletimeout'
])
.
isdigit
():
# idletimeout should be set and we should have collected some session data from the client
# idletimeout should be set and we should have collected some session data from the client
if
int
(
cpzones
[
zoneid
]
[
'idletimeout'
])
>
0
and
float
(
db_client
[
'last_accessed'
])
>
0
:
if
int
(
cpzone_info
[
'idletimeout'
])
>
0
and
float
(
db_client
[
'last_accessed'
])
>
0
:
if
(
time
.
time
()
-
float
(
db_client
[
'last_accessed'
]))
/
60
>
int
(
cpzones
[
zoneid
]
[
'idletimeout'
]):
if
(
time
.
time
()
-
float
(
db_client
[
'last_accessed'
]))
/
60
>
int
(
cpzone_info
[
'idletimeout'
]):
drop_session_reason
=
"session
%
s hit idletimeout"
%
db_client
[
'sessionId'
]
drop_session_reason
=
"session
%
s hit idletimeout"
%
db_client
[
'sessionId'
]
# cleanup concurrent users
# cleanup concurrent users
if
'concurrentlogins'
in
cpzones
[
zoneid
]
and
int
(
cpzones
[
zoneid
]
[
'concurrentlogins'
])
==
0
:
if
'concurrentlogins'
in
cpzone_info
and
int
(
cpzone_info
[
'concurrentlogins'
])
==
0
:
if
db_client
[
'sessionId'
]
in
concurrent_users
:
if
db_client
[
'sessionId'
]
in
concurrent_users
:
drop_session_reason
=
"remove concurrent session
%
s"
%
db_client
[
'sessionId'
]
drop_session_reason
=
"remove concurrent session
%
s"
%
db_client
[
'sessionId'
]
# if mac address changes, drop session. it's not the same client
# if mac address changes, drop session. it's not the same client
current_arp
=
arp
.
get_by_ipaddress
(
db_client
[
'ipAddress'
]
)
current_arp
=
self
.
arp
.
get_by_ipaddress
(
cpnet
)
if
current_arp
is
not
None
and
current_arp
[
'mac'
]
!=
db_client
[
'macAddress'
]:
if
current_arp
is
not
None
and
current_arp
[
'mac'
]
!=
db_client
[
'macAddress'
]:
drop_session_reason
=
"mac address changed for session
%
s"
%
db_client
[
'sessionId'
]
drop_session_reason
=
"mac address changed for session
%
s"
%
db_client
[
'sessionId'
]
elif
db_client
[
'authenticated_via'
]
==
'---mac---'
:
# detect mac changes
current_ip
=
self
.
arp
.
get_address_by_mac
(
db_client
[
'macAddress'
])
if
current_ip
!=
None
:
if
db_client
[
'ipAddress'
]
!=
''
:
# remove old ip
self
.
ipfw
.
delete
(
zoneid
,
db_client
[
'ipAddress'
])
self
.
db
.
update_client_ip
(
zoneid
,
db_client
[
'sessionId'
],
current_ip
)
self
.
ipfw
.
add_to_table
(
zoneid
,
current_ip
)
self
.
ipfw
.
add_accounting
(
current_ip
)
# check session, if it should be active, validate its properties
# check session, if it should be active, validate its properties
if
drop_session_reason
is
None
:
if
drop_session_reason
is
None
:
# registered client, but not active according to ipfw (after reboot)
# registered client, but not active according to ipfw (after reboot)
if
cpnet
not
in
registered_addresses
:
if
cpnet
not
in
registered_addresses
:
ipfw
.
add_to_table
(
zoneid
,
cpnet
)
self
.
ipfw
.
add_to_table
(
zoneid
,
cpnet
)
# is accounting rule still available? need to reapply after reload / reboot
# is accounting rule still available? need to reapply after reload / reboot
if
cpnet
not
in
registered_add_accounting
and
db_client
[
'ipAddress'
]
not
in
registered_add
_accounting
:
if
cpnet
not
in
registered_addr
_accounting
:
ipfw
.
add_accounting
(
cpnet
)
self
.
ipfw
.
add_accounting
(
cpnet
)
else
:
else
:
# remove session
# remove session
syslog
.
syslog
(
syslog
.
LOG_NOTICE
,
drop_session_reason
)
syslog
.
syslog
(
syslog
.
LOG_NOTICE
,
drop_session_reason
)
db
.
del_client
(
zoneid
,
db_client
[
'sessionId'
])
self
.
ipfw
.
delete
(
zoneid
,
cpnet
)
ipfw
.
delete_from_table
(
zoneid
,
cpnet
)
self
.
db
.
del_client
(
zoneid
,
db_client
[
'sessionId'
])
ipfw
.
del_accounting
(
cpnet
)
# if there are addresses/networks in the underlying ipfw table which are not in our administration,
# remove them from ipfw.
for
registered_address
in
registered_addresses
:
address_active
=
False
for
db_client
in
expected_clients
:
if
registered_address
==
db_client
[
'ipAddress'
]:
address_active
=
True
break
if
not
address_active
:
self
.
ipfw
.
delete
(
zoneid
,
registered_address
)
def
main
():
""" Background process loop, runs as backend daemon for all zones. only one should be active at all times.
The main job of this procedure is to sync the administration with the actual situation in the ipfw firewall.
"""
bgprocess
=
CPBackgroundProcess
()
bgprocess
.
initialize_fixed
()
while
True
:
try
:
# open database
bgprocess
.
db
.
open
()
# reload cached arp table contents
bgprocess
.
arp
.
reload
()
# update accounting info, for all zones
bgprocess
.
db
.
update_accounting_info
(
bgprocess
.
ipfw
.
list_accounting_info
())
# process sessions per zone
for
zoneid
in
bgprocess
.
list_zone_ids
():
bgprocess
.
sync_zone
(
zoneid
)
# cl
eanup, destruct
# cl
ose the database handle while waiting for the next poll
del
db
bgprocess
.
db
.
close
()
# sleep
# sleep
time
.
sleep
(
5
)
time
.
sleep
(
5
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment