Skip to content

Commit

Permalink
ffh-check-connection: initial commit
Browse files Browse the repository at this point in the history
This commit adds a new package which can be used for scheduled
connectivity checks.
  • Loading branch information
CodeFetch committed Jun 8, 2021
1 parent 25b56f9 commit 284f435
Show file tree
Hide file tree
Showing 8 changed files with 308 additions and 0 deletions.
23 changes: 23 additions & 0 deletions ffh-check-connection/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Copyright (c) 2021, Freifunk Hannover <info@hannover.freifunk.net>
Copyright (c) 2013-2021, Project Gluon
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
21 changes: 21 additions & 0 deletions ffh-check-connection/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
include $(TOPDIR)/rules.mk

PKG_NAME:=ffh-check-connection
PKG_VERSION:=1
PKG_RELEASE:=1

include $(TOPDIR)/../package/gluon.mk

define Package/$(PKG_NAME)
TITLE:=Helper script to do ping checks with configurable IPv6 targets
DEPENDS:=+gluon-core +libgluonutil +gluon-site +luaposix +luabitop +micrond @GLUON_MULTIDOMAIN
endef

define Package/$(PKG_NAME)/description
Script to check when there is connectivity to configurable targets
possible using ICMP IPv6 pings. This script will be executed every
minute by ``micrond``. If e.g. the connection state changes it
executes corresponding commands in a configurable manner.
endef

$(eval $(call BuildPackageGluon,$(PKG_NAME)))
69 changes: 69 additions & 0 deletions ffh-check-connection/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
ffh-check-connection
====================

This package adds a script that checks if at least one connection to IPv6 hosts
defined as *target groups* is working using the ping command.
The script is called once every minute by ``micrond``.
For example one can define a group of *local* targets to check if a connection
to hosts in the mesh network is possible (e.g. time or update servers) and
*global* targets for checking if a connection to the global internet is possible.
Currently only IPv6 addresses are supported.
This packages is e.g. being used by *ffh-wifi-offline-ssid*.

domain.conf
-----------

Target groups can be pre-defined in the domain config.

::

ffh_check_connection = {
targets = {
targets_local = {
'fe80::dead:c0de:1',
'fe80::bad:c0de:1',
'fe80::dead:c0de:2',
'fe80::bad:c0de:2',
},
targets_global = {
'2620:0:ccc::2', -- OpenDNS
'2001:4860:4860::8888', -- Google DNS
'2600::1', -- Sprint DNS
'2620:0:ccd::2', -- OpenDNS
'2001:4860:4860::8844', -- Google DNS
'2600::2', -- Sprint DNS
},
},
},


Defining target groups in the domain.conf will overwrite existing ones with the same
name when performing a *sysupgrade* or by triggering *gluon-reconfigure*.

Configuration via UCI
---------------------

Packages can use ffh-check-connection to be triggered after connection checks.
For this they can define the following *script* attributes:

script : an entry for defining the ping target
enabled :
- a boolean defining whether the script will be considered
interval :
- the interval to execute the trigger script (in minutes - defaults to 1)
command :
- the command to execute
groups :
- the array of target groups on which the ping test will be performed on
onchange :
- if set true the command is only being executed on a state change or always otherwise
trigger :
- on which the command is being executed (``offline``, ``online`` or unset for both)


*target* groups can be defined with the following attributes:

target : an entry for defining the IPv6 address to ping
hosts :
- array containing the IPv6 addresses to perform the ping test on

5 changes: 5 additions & 0 deletions ffh-check-connection/check_site.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
local function check_target(t)
need_string_array_match(t, '^[%x:]+$', false)
end

need_table(in_domain({'ffh_check_connection', 'targets'}), check_target, false)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* * * * * /usr/sbin/ffh-check-connection
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/lua

local uci = require('simple-uci').cursor()
local site = require 'gluon.site'

for group, hosts in pairs(site.ffh_check_connection.targets()) do
uci:delete('ffh-check-connection', 'target', group)
uci:section('ffh-check-connection', 'target', group, {
hosts = hosts
})
end

uci:save('ffh-check-connection')
176 changes: 176 additions & 0 deletions ffh-check-connection/luasrc/usr/sbin/ffh-check-connection
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
#!/usr/bin/lua

local bit = require('bit')
local fcntl = require('posix.fcntl')
local unistd = require 'posix.unistd'
local util = require 'gluon.util'
local uci = require('simple-uci').cursor()

-- Minimal uptime (in minutes) before the checks start
local min_uptime = 5

local offline_file_prefix = '/tmp/ffh-offline-'
local firstrun_file = '/tmp/ffh-check-connection-firstrun'
local lastrun_file = '/tmp/ffh-check-connection-lastrun'
local lockfile = '/var/lock/ffh-check-connection.lock'

local function readnumber(file)
return tonumber(util.readfile(file))
end

local function writenumber(file, num)
io.open(file, "w"):write(tostring(math.floor(num)))
end

local function shuffle(tbl)
new_tbl = {}
for i, ele in ipairs(tbl) do
table.insert(new_tbl, math.random(1, #new_tbl+1), ele)
end

return new_tbl
end

local function ping_hosts(hosts)
for _, host in ipairs(hosts) do
if 0 == os.execute("ping -c 1 -w 10 " .. host) then
return true
end
end

return false
end

local function check_connection(group, old_state, hosts)
local offline_file = offline_file_prefix .. group
local targets = shuffle(hosts)

if ping_hosts(targets) then
if not old_state then
util.log(group .. 'connectivity available again')
os.remove(offline_file)
end

return true
end

if old_state then
util.log(group .. ' connectivity lost')
writenumber(offline_file, (util.get_uptime() / 60))
end

return false
end

local lockfd, err = fcntl.open(lockfile, bit.bor(fcntl.O_WRONLY, fcntl.O_CREAT), 384) -- mode 0600

if not lockfd then
util.log(err, true)
os.exit(1)
end

local ok, _ = fcntl.fcntl(lockfd, fcntl.F_SETLK, {
l_start = 0,
l_len = 0,
l_type = fcntl.F_WRLCK,
l_whence = unistd.SEEK_SET,
})

if not ok then
io.stderr:write(string.format(
"Unable to lock file %s. Make sure there is no other instance running.\n",
lockfile
))
os.exit(1)
end

local uptime = math.floor(util.get_uptime() / 60)

if uptime < min_uptime then
os.exit(0)
end

math.randomseed(uptime)

local firstrun = uptime
local lastrun = uptime
if unistd.access(firstrun_file) and unistd.access(lastrun_file) then
firstrun = readnumber(firstrun_file)
lastrun = readnumber(lastrun_file)
else
writenumber(firstrun_file, uptime)
end

local runtime = lastrun - firstrun

local scripts = {}
uci:foreach('ffh-check-connection', 'script', function(script)
if not script['enabled'] then return end

if not runtime or uptime - lastrun >= (tonumber(script['interval']) or 1) then
table.insert(scripts, script)
end
end)

local groups = {}
uci:foreach('ffh-check-connection', 'target', function(group)
-- do not perform connection checks for groups which are not in use
for _, script in ipairs(scripts) do
if util.contains(script['groups'], group['.name']) then
groups[group['.name']] = group['hosts']
break
end
end
end)

local old_states = {}
for group, _ in pairs(groups) do
if unistd.access(offline_file_prefix .. group) then
old_states[group] = false
else
old_states[group] = true
end
end

local states = {}
for group, hosts in pairs(groups) do
states[group] = check_connection(group, old_states[group], hosts)
end

for _, script in ipairs(scripts) do
local state_changed = false
local state_offline = false
local state_online = false

for _, group in ipairs(script['groups']) do
if nil ~= states[group] then
if states[group] ~= old_states[group] then
state_changed = true
end

if states[group] then
state_online = true
else
state_offline = true
end
end
end

if not runtime or state_changed or not script['onchange'] then
local do_run = not script['trigger']

if script['trigger'] == 'online' and state_online then
do_run = true
end

if script['trigger'] == 'offline' and state_offline then
do_run = true
end

if do_run then
util.exec(script['command'])
end
end
end

writenumber(lastrun_file, (util.get_uptime() / 60))

0 comments on commit 284f435

Please sign in to comment.