gluon-mesh-babel: add new package
This commit is contained in:
parent
6241ba5435
commit
0f9ab5e306
@ -46,6 +46,22 @@ prefix6
|
|||||||
|
|
||||||
prefix6 = 'fdca::ffee:babe:1::/64'
|
prefix6 = 'fdca::ffee:babe:1::/64'
|
||||||
|
|
||||||
|
node_prefix6
|
||||||
|
The ipv6 prefix from which the unique IP-addresses for nodes are selected
|
||||||
|
in babel-based networks. This may overlap with prefix6. e.g.
|
||||||
|
::
|
||||||
|
|
||||||
|
node_prefix6 = 'fdca::ffee:babe:2::/64'
|
||||||
|
|
||||||
|
node_client_prefix6
|
||||||
|
The ipv6 prefix from which the client-specific IP-address is calculated that
|
||||||
|
is assigned to each node by l3roamd to allow efficient communication when
|
||||||
|
roaming. This is exclusively useful when running a routing mesh protocol
|
||||||
|
like babel. e.g.
|
||||||
|
::
|
||||||
|
|
||||||
|
node_client_prefix6 = 'fdca::ffee:babe:3::/64'
|
||||||
|
|
||||||
timezone
|
timezone
|
||||||
The timezone of your community live in, e.g.
|
The timezone of your community live in, e.g.
|
||||||
::
|
::
|
||||||
@ -260,6 +276,9 @@ mesh_vpn
|
|||||||
and *gluon-mesh-vpn-tunneldigger* should be installed with the current
|
and *gluon-mesh-vpn-tunneldigger* should be installed with the current
|
||||||
implementation.
|
implementation.
|
||||||
|
|
||||||
|
**Note:** It may be interesting to include the package *gluon-iptables-clamp-mss-to-pmtu*
|
||||||
|
in the build when using *gluon-mesh-babel* to work around icmp blackholes on the internet.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
mesh_vpn = {
|
mesh_vpn = {
|
||||||
|
27
package/gluon-mesh-babel/Makefile
Normal file
27
package/gluon-mesh-babel/Makefile
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=gluon-mesh-babel
|
||||||
|
PKG_VERSION:=1
|
||||||
|
|
||||||
|
PKG_BUILD_DEPENDS := libbabelhelper
|
||||||
|
PKG_BUILD_DEPENDS += libjson-c
|
||||||
|
|
||||||
|
include ../gluon.mk
|
||||||
|
|
||||||
|
define Package/gluon-mesh-babel
|
||||||
|
TITLE:=Babel mesh
|
||||||
|
DEPENDS:=+gluon-core +babeld +mmfd +libiwinfo +libgluonutil +firewall +libjson-c +libnl-tiny +libubus +libubox +libblobmsg-json +libbabelhelper +luabitop
|
||||||
|
PROVIDES:=gluon-mesh-provider
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/gluon-mesh-babel/install
|
||||||
|
$(Gluon/Build/Install)
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/usr/bin
|
||||||
|
$(INSTALL_DIR) $(1)/usr/lib/respondd
|
||||||
|
$(CP) $(PKG_BUILD_DIR)/babel-respondd.so $(1)/usr/lib/respondd/mesh-babel.so
|
||||||
|
$(INSTALL_DIR) $(1)/lib/gluon/status-page/providers
|
||||||
|
$(INSTALL_BIN) $(PKG_BUILD_DIR)/neighbours-babel $(1)/lib/gluon/status-page/providers/
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackageGluon,gluon-mesh-babel))
|
7
package/gluon-mesh-babel/check_site.lua
Normal file
7
package/gluon-mesh-babel/check_site.lua
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
need_string_match(in_domain({'node_prefix6'}), '^[%x:]+/64$')
|
||||||
|
need_string_match(in_domain({'node_client_prefix6'}), '^[%x:]+/64$')
|
||||||
|
|
||||||
|
need_string_match(in_domain({'next_node', 'ip6'}), '^[%x:]+$', false)
|
||||||
|
need_string_match(in_domain({'next_node', 'ip4'}), '^%d+.%d+.%d+.%d+$', false)
|
||||||
|
|
||||||
|
need_string_match(in_domain({'next_node', 'mac'}), '^%x[02468aAcCeE]:%x%x:%x%x:%x%x:%x%x:%x%x$', false)
|
@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
case "$ACTION"
|
||||||
|
in
|
||||||
|
ifup)
|
||||||
|
echo 1 > /sys/devices/virtual/net/br-client/bridge/multicast_snooping
|
||||||
|
echo 2 > /sys/devices/virtual/net/br-client/bridge/multicast_router
|
||||||
|
;;
|
||||||
|
esac
|
78
package/gluon-mesh-babel/files/etc/init.d/gluon-mesh-babel
Executable file
78
package/gluon-mesh-babel/files/etc/init.d/gluon-mesh-babel
Executable file
@ -0,0 +1,78 @@
|
|||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
USE_PROCD=1
|
||||||
|
START=45
|
||||||
|
PORT=33123
|
||||||
|
|
||||||
|
DAEMON=/usr/sbin/babeld
|
||||||
|
pidfile=/var/run/gluon-babeld.pid
|
||||||
|
CONFIGFILE=/etc/gluon-babeld.conf
|
||||||
|
BABELOPTSFILE=/tmp/addn-babelopts
|
||||||
|
touch $BABELOPTSFILE
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
procd_open_instance
|
||||||
|
procd_set_param command $DAEMON
|
||||||
|
procd_append_param command -D -I "$pidfile" -G "$PORT" -c "$CONFIGFILE" $(cat $BABELOPTSFILE) babeldummydoesnotexist
|
||||||
|
procd_set_param respawn ${respawn_threshold:-60} ${respawn_timeout:-5} ${respawn_retry:-0}
|
||||||
|
procd_set_param stderr 1
|
||||||
|
procd_set_param stdout 1
|
||||||
|
procd_close_instance
|
||||||
|
}
|
||||||
|
|
||||||
|
echotobabel() {
|
||||||
|
local count=0
|
||||||
|
local line="$1"
|
||||||
|
while ! (echo -e "$line" | nc ::1 "$PORT" >/dev/null 2>&1)
|
||||||
|
do
|
||||||
|
sleep 1
|
||||||
|
echo retrying to connect to babeld in PID $$, waited ${count}s >&2
|
||||||
|
count=$((count+1))
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
waitforsocket() {
|
||||||
|
echotobabel "dump"
|
||||||
|
[ $? -gt 0 ] && { echo "Failed to connect to babeld socket on port $PORT, assuming the service was not started properly"; exit 43; }
|
||||||
|
}
|
||||||
|
|
||||||
|
reload_service() {
|
||||||
|
waitforsocket
|
||||||
|
|
||||||
|
for i in $(ubus call network.interface dump | jsonfilter -e "@.interface[@.proto='gluon_mesh' && @.up=true].device")
|
||||||
|
do
|
||||||
|
if ! echotobabel dump|grep "add interface"|grep -q $i
|
||||||
|
then
|
||||||
|
echotobabel "interface $i update-interval 300"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
for i in $(echotobabel "dump"|grep "add interface"|cut -d" " -f3)
|
||||||
|
do
|
||||||
|
if ! ubus call network.interface dump | jsonfilter -e "@.interface[@.proto='gluon_mesh' && @.up=true].device"|grep -q $i
|
||||||
|
then
|
||||||
|
echotobabel "flush interface $i"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
service_triggers() {
|
||||||
|
local script=$(readlink "$initscript")
|
||||||
|
local name=$(basename "${script:-$initscript}")
|
||||||
|
|
||||||
|
procd_open_trigger
|
||||||
|
procd_add_raw_trigger "interface.*" 0 "/etc/init.d/$name" reload
|
||||||
|
procd_close_trigger
|
||||||
|
}
|
||||||
|
|
||||||
|
service_started() {
|
||||||
|
# make sure the init script does not finish until babeld is actually up.
|
||||||
|
# unfortunately procd will still start multiple instances of the same script which is why waitforsocket is also run on reload
|
||||||
|
waitforsocket
|
||||||
|
}
|
||||||
|
stop_service(){
|
||||||
|
kill $(pgrep -P 1 babeld)
|
||||||
|
}
|
||||||
|
status() {
|
||||||
|
kill -USR1 $(pgrep -P 1 babeld)
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
mmfd
|
7
package/gluon-mesh-babel/luasrc/lib/gluon/radvd/arguments
Executable file
7
package/gluon-mesh-babel/luasrc/lib/gluon/radvd/arguments
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
local site = require "gluon.site"
|
||||||
|
|
||||||
|
io.write("-i local-node --default-lifetime 900 -a " .. site.prefix6())
|
||||||
|
if site.dns() and site.dns.servers() then
|
||||||
|
io.write(" --rdnss " .. site.next_node.ip6())
|
||||||
|
end
|
103
package/gluon-mesh-babel/luasrc/lib/gluon/upgrade/140-gluon-mesh-babel-firewall
Executable file
103
package/gluon-mesh-babel/luasrc/lib/gluon/upgrade/140-gluon-mesh-babel-firewall
Executable file
@ -0,0 +1,103 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
|
||||||
|
local uci = require('simple-uci').cursor()
|
||||||
|
local site = require "gluon.site"
|
||||||
|
|
||||||
|
uci:section('firewall', 'zone', 'l3roamd', {
|
||||||
|
name = 'l3roamd',
|
||||||
|
input = 'ACCEPT',
|
||||||
|
output = 'ACCEPT',
|
||||||
|
forward = 'REJECT',
|
||||||
|
device = 'l3roam+',
|
||||||
|
log = '1',
|
||||||
|
})
|
||||||
|
|
||||||
|
uci:section('firewall', 'zone', 'mmfd', {
|
||||||
|
name = 'mmfd',
|
||||||
|
input = 'REJECT',
|
||||||
|
output = 'accept',
|
||||||
|
forward = 'REJECT',
|
||||||
|
device = 'mmfd+',
|
||||||
|
log = '1',
|
||||||
|
})
|
||||||
|
|
||||||
|
-- forwardings and respective rules
|
||||||
|
uci:section('firewall', 'forwarding', 'fcc', {
|
||||||
|
src = 'local_client',
|
||||||
|
dest = 'local_client',
|
||||||
|
})
|
||||||
|
|
||||||
|
uci:section('firewall', 'forwarding', 'fcm', {
|
||||||
|
src = 'local_client',
|
||||||
|
dest = 'mesh',
|
||||||
|
})
|
||||||
|
|
||||||
|
uci:section('firewall', 'forwarding', 'fmc', {
|
||||||
|
src = 'mesh',
|
||||||
|
dest = 'local_client',
|
||||||
|
})
|
||||||
|
|
||||||
|
uci:section('firewall', 'forwarding', 'fmm', {
|
||||||
|
src = 'mesh',
|
||||||
|
dest = 'mesh',
|
||||||
|
})
|
||||||
|
|
||||||
|
uci:section('firewall', 'forwarding', 'flc', {
|
||||||
|
src = 'l3roamd',
|
||||||
|
dest = 'local_client',
|
||||||
|
})
|
||||||
|
|
||||||
|
uci:section('firewall', 'forwarding', 'fcl', {
|
||||||
|
src = 'local_client',
|
||||||
|
dest = 'l3roamd',
|
||||||
|
})
|
||||||
|
|
||||||
|
uci:section('firewall', 'rule', 'mesh_respondd_mcast_ll', {
|
||||||
|
src = 'mesh',
|
||||||
|
src_ip = 'fe80::/64' ,
|
||||||
|
dest_port = '1001',
|
||||||
|
proto = 'udp',
|
||||||
|
target = 'ACCEPT',
|
||||||
|
})
|
||||||
|
|
||||||
|
uci:section('firewall', 'rule', 'mesh_respondd_mcast2', {
|
||||||
|
src = 'mesh',
|
||||||
|
src_ip = site.node_prefix6(),
|
||||||
|
dest_port = '1001',
|
||||||
|
proto = 'udp',
|
||||||
|
target = 'ACCEPT',
|
||||||
|
})
|
||||||
|
|
||||||
|
uci:section('firewall', 'rule', 'mmfd_respondd_ll', {
|
||||||
|
src = 'mmfd',
|
||||||
|
src_ip = 'fe80::/64',
|
||||||
|
dest_port = '1001',
|
||||||
|
proto = 'udp',
|
||||||
|
target = 'ACCEPT',
|
||||||
|
})
|
||||||
|
|
||||||
|
uci:section('firewall', 'rule', 'mmfd_respondd_mesh', {
|
||||||
|
src = 'mmfd',
|
||||||
|
src_ip = site.node_prefix6(),
|
||||||
|
dest_port = '1001',
|
||||||
|
proto = 'udp',
|
||||||
|
target = 'ACCEPT',
|
||||||
|
})
|
||||||
|
|
||||||
|
uci:section('firewall', 'rule', 'mesh_mmfd', {
|
||||||
|
src = 'mesh',
|
||||||
|
src_ip = 'fe80::/64',
|
||||||
|
dest_port = '27275',
|
||||||
|
proto = 'udp',
|
||||||
|
target = 'ACCEPT',
|
||||||
|
})
|
||||||
|
|
||||||
|
uci:section('firewall', 'rule', 'mesh_babel', {
|
||||||
|
src = 'mesh',
|
||||||
|
src_ip = 'fe80::/64',
|
||||||
|
dest_port = '6696',
|
||||||
|
proto = 'udp',
|
||||||
|
target = 'ACCEPT',
|
||||||
|
})
|
||||||
|
|
||||||
|
uci:save('firewall')
|
102
package/gluon-mesh-babel/luasrc/lib/gluon/upgrade/300-gluon-mesh-babel-ip6
Executable file
102
package/gluon-mesh-babel/luasrc/lib/gluon/upgrade/300-gluon-mesh-babel-ip6
Executable file
@ -0,0 +1,102 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
|
||||||
|
local bit = require 'bit'
|
||||||
|
local sysconfig = require 'gluon.sysconfig'
|
||||||
|
local uci = require('simple-uci').cursor()
|
||||||
|
local site = require 'gluon.site'
|
||||||
|
|
||||||
|
|
||||||
|
function IPv6(address)
|
||||||
|
--[[
|
||||||
|
(c) 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net>
|
||||||
|
(c) 2008 Steven Barth <steven@midlink.org>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License").
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
]]--
|
||||||
|
local data = {}
|
||||||
|
|
||||||
|
local borderl = address:sub(1, 1) == ":" and 2 or 1
|
||||||
|
local borderh, zeroh, chunk, block
|
||||||
|
|
||||||
|
if #address > 45 then return nil end
|
||||||
|
|
||||||
|
repeat
|
||||||
|
borderh = address:find(":", borderl, true)
|
||||||
|
if not borderh then break end
|
||||||
|
|
||||||
|
block = tonumber(address:sub(borderl, borderh - 1), 16)
|
||||||
|
if block and block <= 0xFFFF then
|
||||||
|
data[#data+1] = block
|
||||||
|
else
|
||||||
|
if zeroh or borderh - borderl > 1 then return nil end
|
||||||
|
zeroh = #data + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
borderl = borderh + 1
|
||||||
|
until #data == 7
|
||||||
|
|
||||||
|
chunk = address:sub(borderl)
|
||||||
|
if #chunk > 0 and #chunk <= 4 then
|
||||||
|
block = tonumber(chunk, 16)
|
||||||
|
if not block or block > 0xFFFF then return nil end
|
||||||
|
|
||||||
|
data[#data+1] = block
|
||||||
|
elseif #chunk > 4 then
|
||||||
|
if #data == 7 or #chunk > 15 then return nil end
|
||||||
|
borderl = 1
|
||||||
|
for i=1, 4 do
|
||||||
|
borderh = chunk:find(".", borderl, true)
|
||||||
|
if not borderh and i < 4 then return nil end
|
||||||
|
borderh = borderh and borderh - 1
|
||||||
|
|
||||||
|
block = tonumber(chunk:sub(borderl, borderh))
|
||||||
|
if not block or block > 255 then return nil end
|
||||||
|
|
||||||
|
if i == 1 or i == 3 then
|
||||||
|
data[#data+1] = block * 256
|
||||||
|
else
|
||||||
|
data[#data] = data[#data] + block
|
||||||
|
end
|
||||||
|
|
||||||
|
borderl = borderh and borderh + 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if zeroh then
|
||||||
|
if #data == 8 then return nil end
|
||||||
|
while #data < 8 do
|
||||||
|
table.insert(data, zeroh, 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #data == 8 then
|
||||||
|
return data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mac_to_ip(prefix, mac)
|
||||||
|
local m1, m2, m3, m6, m7, m8 = string.match(mac, '(%x%x):(%x%x):(%x%x):(%x%x):(%x%x):(%x%x)')
|
||||||
|
local m4 = 0xff
|
||||||
|
local m5 = 0xfe
|
||||||
|
m1 = bit.bxor(tonumber(m1, 16), 0x02)
|
||||||
|
|
||||||
|
local h1 = 0x100 * m1 + tonumber(m2, 16)
|
||||||
|
local h2 = 0x100 * tonumber(m3, 16) + m4
|
||||||
|
local h3 = 0x100 * m5 + tonumber(m6, 16)
|
||||||
|
local h4 = 0x100 * tonumber(m7, 16) + tonumber(m8, 16)
|
||||||
|
|
||||||
|
local prefix, plen = string.match(prefix, '(.*)/(%d+)')
|
||||||
|
plen = tonumber(plen, 10)
|
||||||
|
|
||||||
|
local p1, p2, p3, p4, p5, p6, p7, p8 = IPv6(prefix)
|
||||||
|
|
||||||
|
return string.format("%x:%x:%x:%x:%x:%x:%x:%x/%d", p1, p2, p3, p4, h1, h2, h3, h4, 128)
|
||||||
|
end
|
||||||
|
|
||||||
|
local ip = mac_to_ip(site.node_prefix6(), sysconfig.primary_mac)
|
||||||
|
|
||||||
|
uci:set('network', 'loopback', 'ip6addr', ip)
|
||||||
|
uci:save('network')
|
@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
|
||||||
|
local site = require 'gluon.site'
|
||||||
|
local babelconf='/etc/gluon-babeld.conf'
|
||||||
|
|
||||||
|
file = io.open(babelconf, "w")
|
||||||
|
file:write("ipv6-subtrees true\n")
|
||||||
|
file:write("reflect-kernel-metric true\n")
|
||||||
|
file:write("export-table 254\n")
|
||||||
|
file:write("log-file /dev/stderr\n")
|
||||||
|
file:write("import-table 254\n")
|
||||||
|
|
||||||
|
file:write("out ip " .. site.next_node.ip6() .. "/128 deny\n")
|
||||||
|
file:write("redistribute ip " .. site.next_node.ip6() .. "/128 deny\n")
|
||||||
|
file:write("redistribute ip " .. site.prefix6() .. " eq 128 allow\n")
|
||||||
|
file:write("redistribute ip " .. site.node_client_prefix6() .. " eq 128 allow\n")
|
||||||
|
file:write("redistribute ip " .. site.node_prefix6() .. " eq 128 allow\n")
|
||||||
|
file:write("redistribute local if br-wan deny\n")
|
||||||
|
file:write("redistribute local ip 0.0.0.0/0 deny\n")
|
||||||
|
file:close()
|
@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/lua
|
||||||
|
|
||||||
|
local uci = require('simple-uci').cursor()
|
||||||
|
|
||||||
|
uci:delete('network', 'mmfd')
|
||||||
|
uci:section('network', 'interface', 'mmfd', {
|
||||||
|
proto = 'static',
|
||||||
|
ifname = 'mmfd0',
|
||||||
|
ip6addr = 'fe80::1/64'
|
||||||
|
})
|
||||||
|
uci:save('network')
|
31
package/gluon-mesh-babel/src/Makefile
Normal file
31
package/gluon-mesh-babel/src/Makefile
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
all: babel-respondd.so neighbours-babel
|
||||||
|
|
||||||
|
CFLAGS += -Wall -g -fPIC -D_GNU_SOURCE
|
||||||
|
|
||||||
|
ifeq ($(origin PKG_CONFIG), undefined)
|
||||||
|
PKG_CONFIG = pkg-config
|
||||||
|
ifeq ($(shell which $(PKG_CONFIG) 2>/dev/null),)
|
||||||
|
$(error $(PKG_CONFIG) not found)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(origin LIBBABEL_CFLAGS) $(origin LIBBABEL_LDLIBS), undefined undefined)
|
||||||
|
LIBBABEL_NAME ?= libbabelhelper
|
||||||
|
ifeq ($(shell $(PKG_CONFIG) --modversion $(LIBBABEL_NAME) 2>/dev/null),)
|
||||||
|
$(error No $(LIBBABEL_NAME) development libraries found!)
|
||||||
|
endif
|
||||||
|
LIBBABEL_CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBBABEL_NAME))
|
||||||
|
LIBBABEL_LDLIBS += $(shell $(PKG_CONFIG) --libs $(LIBBABEL_NAME))
|
||||||
|
endif
|
||||||
|
CFLAGS += $(LIBBABEL_CFLAGS)
|
||||||
|
LDLIBS += $(LIBBABEL_LDLIBS)
|
||||||
|
|
||||||
|
CFLAGS_JSONC = $(shell pkg-config --cflags json-c)
|
||||||
|
LDFLAGS_JSONC = $(shell pkg-config --libs json-c)
|
||||||
|
|
||||||
|
|
||||||
|
babel-respondd.so: babel-respondd.c handle_neighbour.c
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -shared $(LDFLAGS_JSONC) -o $@ $^ -lgluonutil -lblobmsg_json -lubox -lubus -liwinfo -luci
|
||||||
|
|
||||||
|
neighbours-babel: neighbours-babel.c handle_neighbour.c
|
||||||
|
$(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_JSONC) $(LDFLAGS) $(LDLIBS) $(LDFLAGS_JSONC) -o $@ $^
|
713
package/gluon-mesh-babel/src/babel-respondd.c
Normal file
713
package/gluon-mesh-babel/src/babel-respondd.c
Normal file
@ -0,0 +1,713 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2016, Matthias Schiffer <mschiffer@universe-factory.net>
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <respondd.h>
|
||||||
|
|
||||||
|
#include <iwinfo.h>
|
||||||
|
#include <json-c/json.h>
|
||||||
|
#include <libgluonutil.h>
|
||||||
|
#include <uci.h>
|
||||||
|
|
||||||
|
#include <alloca.h>
|
||||||
|
#include <glob.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
|
#include <linux/ethtool.h>
|
||||||
|
#include <linux/if_addr.h>
|
||||||
|
#include <linux/sockios.h>
|
||||||
|
|
||||||
|
#include <netdb.h>
|
||||||
|
#include "errno.h"
|
||||||
|
#include <libbabelhelper/babelhelper.h>
|
||||||
|
|
||||||
|
#include <libubox/blobmsg_json.h>
|
||||||
|
#include "libubus.h"
|
||||||
|
|
||||||
|
#define _STRINGIFY(s) #s
|
||||||
|
#define STRINGIFY(s) _STRINGIFY(s)
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define SOCKET_INPUT_BUFFER_SIZE 255
|
||||||
|
#define BABEL_PORT 33123
|
||||||
|
#define VPN_INTERFACE "mesh-vpn"
|
||||||
|
#define l3rdctl "/var/run/l3roamd.sock"
|
||||||
|
|
||||||
|
#define IFNAMELEN 32
|
||||||
|
#define PROTOLEN 32
|
||||||
|
|
||||||
|
#define UBUS_TIMEOUT 30
|
||||||
|
#define UBUS_SOCKET "/var/run/ubus.sock"
|
||||||
|
|
||||||
|
static struct babelhelper_ctx bhelper_ctx = {};
|
||||||
|
|
||||||
|
static int obtain_ifmac(unsigned char *ifmac, const char *ifname) {
|
||||||
|
struct ifreq ifr = {};
|
||||||
|
int sock;
|
||||||
|
|
||||||
|
sock=socket(PF_INET, SOCK_STREAM, 0);
|
||||||
|
if (-1==sock) {
|
||||||
|
perror("socket() ");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1);
|
||||||
|
|
||||||
|
printf("obtaining hw address for nic: %s %s\n", ifname, ifr.ifr_name);
|
||||||
|
if (-1==ioctl(sock, SIOCGIFHWADDR, &ifr)) {
|
||||||
|
perror("ioctl(SIOCGIFHWADDR) ");
|
||||||
|
close(sock);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
close(sock);
|
||||||
|
|
||||||
|
memcpy(ifmac, ifr.ifr_hwaddr.sa_data, 6);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* get_line_from_run(const char* command) {
|
||||||
|
FILE *fp;
|
||||||
|
char *line = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
fp = popen(command, "r");
|
||||||
|
|
||||||
|
if (fp != NULL) {
|
||||||
|
ssize_t r = getline(&line, &len, fp);
|
||||||
|
if (r >= 0) {
|
||||||
|
len = strlen(line);
|
||||||
|
|
||||||
|
if (len && line[len-1] == '\n')
|
||||||
|
line[len-1] = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
free(line);
|
||||||
|
line = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pclose(fp);
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct json_object * get_addresses(void) {
|
||||||
|
char *primarymac = gluonutil_get_sysconfig("primary_mac");
|
||||||
|
char *address = malloc(INET6_ADDRSTRLEN+1);
|
||||||
|
char node_prefix_str[INET6_ADDRSTRLEN+1];
|
||||||
|
struct in6_addr node_prefix = {};
|
||||||
|
struct json_object *retval = json_object_new_array();
|
||||||
|
|
||||||
|
if (!gluonutil_get_node_prefix6(&node_prefix)) {
|
||||||
|
fprintf(stderr, "get_addresses: could not obtain mesh-prefix from site.conf. Not adding addresses to json data\n");
|
||||||
|
goto free;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inet_ntop(AF_INET6, &(node_prefix.s6_addr), node_prefix_str, INET6_ADDRSTRLEN) == NULL) {
|
||||||
|
fprintf(stderr, "get_addresses: could not convert mesh-prefix from site.conf to string\n");
|
||||||
|
goto free;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *prefix_addresspart = strndup(node_prefix_str, INET6_ADDRSTRLEN);
|
||||||
|
if (! babelhelper_generateip_str(address, primarymac, prefix_addresspart) ) {
|
||||||
|
fprintf(stderr, "IP-address could not be generated by babelhelper");
|
||||||
|
}
|
||||||
|
free(prefix_addresspart);
|
||||||
|
|
||||||
|
json_object_array_add(retval, json_object_new_string(address));
|
||||||
|
|
||||||
|
free:
|
||||||
|
free(address);
|
||||||
|
free(primarymac);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool interface_file_exists(const char *ifname, const char *name) {
|
||||||
|
const char *format = "/sys/class/net/%s/%s";
|
||||||
|
char path[strlen(format) + strlen(ifname) + strlen(name)+1];
|
||||||
|
snprintf(path, sizeof(path), format, ifname, name);
|
||||||
|
|
||||||
|
return !access(path, F_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct in6_addr mac2ipv6(uint8_t mac[6], char * prefix) {
|
||||||
|
struct in6_addr address = {};
|
||||||
|
inet_pton(AF_INET6, prefix, &address);
|
||||||
|
|
||||||
|
address.s6_addr[8] = mac[0] ^ 0x02;
|
||||||
|
address.s6_addr[9] = mac[1];
|
||||||
|
address.s6_addr[10] = mac[2];
|
||||||
|
address.s6_addr[11] = 0xff;
|
||||||
|
address.s6_addr[12] = 0xfe;
|
||||||
|
address.s6_addr[13] = mac[3];
|
||||||
|
address.s6_addr[14] = mac[4];
|
||||||
|
address.s6_addr[15] = mac[5];
|
||||||
|
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mesh_add_if(const char *ifname, struct json_object *wireless,
|
||||||
|
struct json_object *tunnel, struct json_object *other) {
|
||||||
|
char str_ip[INET6_ADDRSTRLEN] = {};
|
||||||
|
unsigned char mac[6] = {};
|
||||||
|
|
||||||
|
if (obtain_ifmac(mac, ifname)) {
|
||||||
|
printf("could not obtain mac for device: %s", ifname);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct in6_addr lladdr = mac2ipv6(mac, "fe80::");
|
||||||
|
inet_ntop(AF_INET6, &lladdr.s6_addr, str_ip, INET6_ADDRSTRLEN);
|
||||||
|
|
||||||
|
struct json_object *address = json_object_new_string(str_ip);
|
||||||
|
|
||||||
|
if (interface_file_exists(ifname, "wireless"))
|
||||||
|
json_object_array_add(wireless, address);
|
||||||
|
else if (interface_file_exists(ifname, "tun_flags"))
|
||||||
|
json_object_array_add(tunnel, address);
|
||||||
|
else
|
||||||
|
json_object_array_add(other, address);
|
||||||
|
|
||||||
|
}
|
||||||
|
static bool handle_neighbour(char **data, void *obj) {
|
||||||
|
if (data[NEIGHBOUR]) {
|
||||||
|
struct json_object *neigh = json_object_new_object();
|
||||||
|
|
||||||
|
if (data[RXCOST])
|
||||||
|
json_object_object_add(neigh, "rxcost", json_object_new_int(atoi(data[RXCOST])));
|
||||||
|
if (data[TXCOST])
|
||||||
|
json_object_object_add(neigh, "txcost", json_object_new_int(atoi(data[TXCOST])));
|
||||||
|
if (data[COST])
|
||||||
|
json_object_object_add(neigh, "cost", json_object_new_int(atoi(data[COST])));
|
||||||
|
if (data[REACH])
|
||||||
|
json_object_object_add(neigh, "reachability", json_object_new_double(strtod(data[REACH], NULL)));
|
||||||
|
|
||||||
|
struct json_object *nif = 0;
|
||||||
|
if (data[IF] && !json_object_object_get_ex(obj, data[IF], &nif)) {
|
||||||
|
nif = json_object_new_object();
|
||||||
|
|
||||||
|
unsigned char ifmac[6] = {};
|
||||||
|
char str_ip[INET6_ADDRSTRLEN] = {};
|
||||||
|
|
||||||
|
if (obtain_ifmac(ifmac, (const char*)data[IF])) {
|
||||||
|
printf("could not obtain mac for device: %s", data[IF]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
struct in6_addr lladdr = mac2ipv6(ifmac, "fe80::");
|
||||||
|
inet_ntop(AF_INET6, &lladdr.s6_addr, str_ip, INET6_ADDRSTRLEN);
|
||||||
|
|
||||||
|
json_object_object_add(nif, "ll-addr", json_object_new_string(str_ip));
|
||||||
|
json_object_object_add(nif, "protocol", json_object_new_string("babel"));
|
||||||
|
json_object_object_add(obj, data[IF], nif);
|
||||||
|
|
||||||
|
}
|
||||||
|
struct json_object *neighborcollector = 0;
|
||||||
|
if (!json_object_object_get_ex(nif, "neighbours", &neighborcollector)) {
|
||||||
|
neighborcollector = json_object_new_object();
|
||||||
|
json_object_object_add(nif, "neighbours", neighborcollector);
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object_object_add(neighborcollector, data[ADDRESS], neigh);
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct json_object * get_babel_neighbours(void) {
|
||||||
|
|
||||||
|
struct json_object *neighbours;
|
||||||
|
neighbours = json_object_new_object();
|
||||||
|
if (!neighbours)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
babelhelper_readbabeldata(&bhelper_ctx, (void*)neighbours, handle_neighbour);
|
||||||
|
|
||||||
|
return(neighbours);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void blobmsg_handle_list(struct blob_attr *attr, int len, bool array, struct json_object *wireless, struct json_object *tunnel, struct json_object *other);
|
||||||
|
|
||||||
|
static void blobmsg_handle_element(struct blob_attr *attr, bool head, char **ifname, char **proto, struct json_object *wireless, struct json_object *tunnel, struct json_object *other) {
|
||||||
|
void *data;
|
||||||
|
|
||||||
|
if (!blobmsg_check_attr(attr, false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
data = blobmsg_data(attr);
|
||||||
|
|
||||||
|
switch (blob_id(attr)) {
|
||||||
|
case BLOBMSG_TYPE_STRING:
|
||||||
|
if (!strncmp(blobmsg_name(attr),"device", 6)) {
|
||||||
|
free(*ifname);
|
||||||
|
*ifname = strndup(data, IFNAMELEN);
|
||||||
|
} else if (!strncmp(blobmsg_name(attr), "proto", 5)) {
|
||||||
|
free(*proto);
|
||||||
|
*proto = strndup(data, PROTOLEN);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case BLOBMSG_TYPE_ARRAY:
|
||||||
|
blobmsg_handle_list(data, blobmsg_data_len(attr), true, wireless, tunnel, other);
|
||||||
|
return;
|
||||||
|
case BLOBMSG_TYPE_TABLE:
|
||||||
|
blobmsg_handle_list(data, blobmsg_data_len(attr), false, wireless, tunnel, other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void blobmsg_handle_list(struct blob_attr *attr, int len, bool array, struct json_object *wireless, struct json_object *tunnel, struct json_object *other) {
|
||||||
|
struct blob_attr *pos;
|
||||||
|
int rem = len;
|
||||||
|
|
||||||
|
char *ifname = NULL;
|
||||||
|
char *proto = NULL;
|
||||||
|
|
||||||
|
__blob_for_each_attr(pos, attr, rem) {
|
||||||
|
blobmsg_handle_element(pos, array, &ifname, &proto, wireless, tunnel, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ifname && proto) {
|
||||||
|
if (!strncmp(proto, "gluon_mesh", 10)) {
|
||||||
|
mesh_add_if(ifname, wireless, tunnel, other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(ifname);
|
||||||
|
free(proto);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void receive_call_result_data(struct ubus_request *req, int type, struct blob_attr *msg) {
|
||||||
|
struct json_object *ret = json_object_new_object();
|
||||||
|
struct json_object *wireless = json_object_new_array();
|
||||||
|
struct json_object *tunnel = json_object_new_array();
|
||||||
|
struct json_object *other = json_object_new_array();
|
||||||
|
|
||||||
|
if (!ret || !wireless || !tunnel || !other) {
|
||||||
|
json_object_put(wireless);
|
||||||
|
json_object_put(tunnel);
|
||||||
|
json_object_put(other);
|
||||||
|
json_object_put(ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!msg) {
|
||||||
|
printf("empty message\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
blobmsg_handle_list(blobmsg_data(msg), blobmsg_data_len(msg), false, wireless, tunnel, other);
|
||||||
|
|
||||||
|
json_object_object_add(ret, "wireless", wireless);
|
||||||
|
json_object_object_add(ret, "tunnel", tunnel);
|
||||||
|
json_object_object_add(ret, "other", other);
|
||||||
|
|
||||||
|
*((struct json_object**)(req->priv)) = ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct json_object * get_mesh_ifs() {
|
||||||
|
struct ubus_context *ubus_ctx;
|
||||||
|
struct json_object *ret = NULL;
|
||||||
|
struct blob_buf b = {};
|
||||||
|
|
||||||
|
unsigned int id=8;
|
||||||
|
|
||||||
|
ubus_ctx = ubus_connect(UBUS_SOCKET);
|
||||||
|
if (!ubus_ctx) {
|
||||||
|
fprintf(stderr,"could not connect to ubus, not providing mesh-data\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uret = -2;
|
||||||
|
blob_buf_init(&b, 0);
|
||||||
|
ubus_lookup_id(ubus_ctx, "network.interface", &id);
|
||||||
|
uret = ubus_invoke(ubus_ctx, id, "dump", b.head, receive_call_result_data, &ret, UBUS_TIMEOUT * 1000);
|
||||||
|
|
||||||
|
if (uret > 0)
|
||||||
|
fprintf(stderr, "ubus command failed: %s\n", ubus_strerror(uret));
|
||||||
|
else if (uret == -2)
|
||||||
|
fprintf(stderr, "invalid call, exiting\n");
|
||||||
|
|
||||||
|
blob_buf_free(&b);
|
||||||
|
|
||||||
|
end:
|
||||||
|
ubus_free(ubus_ctx);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct json_object * get_mesh(void) {
|
||||||
|
struct json_object *ret = json_object_new_object();
|
||||||
|
struct json_object *interfaces = NULL;
|
||||||
|
interfaces = json_object_new_object();
|
||||||
|
json_object_object_add(interfaces, "interfaces", get_mesh_ifs());
|
||||||
|
json_object_object_add(ret, "babel", interfaces);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct json_object * get_babeld_version(void) {
|
||||||
|
char *version = get_line_from_run("exec babeld -V 2>&1");
|
||||||
|
struct json_object *ret = gluonutil_wrap_string(version);
|
||||||
|
free(version);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct json_object * respondd_provider_nodeinfo(void) {
|
||||||
|
bhelper_ctx.debug=false;
|
||||||
|
struct json_object *ret = json_object_new_object();
|
||||||
|
|
||||||
|
struct json_object *network = json_object_new_object();
|
||||||
|
json_object_object_add(network, "addresses", get_addresses());
|
||||||
|
json_object_object_add(network, "mesh", get_mesh());
|
||||||
|
json_object_object_add(ret, "network", network);
|
||||||
|
|
||||||
|
struct json_object *software = json_object_new_object();
|
||||||
|
struct json_object *software_babeld = json_object_new_object();
|
||||||
|
json_object_object_add(software_babeld, "version", get_babeld_version());
|
||||||
|
json_object_object_add(software, "babeld", software_babeld);
|
||||||
|
json_object_object_add(ret, "software", software);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t getnumber(const char *ifname, const char *stat) {
|
||||||
|
const char *format = "/sys/class/net/%s/statistics/%s";
|
||||||
|
char path[strlen(format) + strlen(ifname) + strlen(stat)];
|
||||||
|
snprintf(path, sizeof(path), format, ifname, stat);
|
||||||
|
if (! access(path, F_OK))
|
||||||
|
{
|
||||||
|
char *line=gluonutil_read_line(path);
|
||||||
|
long long i = atoll(line);
|
||||||
|
free(line);
|
||||||
|
return(i);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct json_object * get_traffic(void) {
|
||||||
|
char ifname[16];
|
||||||
|
|
||||||
|
strncpy(ifname, "br-client", 16);
|
||||||
|
|
||||||
|
struct json_object *ret = NULL;
|
||||||
|
struct json_object *rx = json_object_new_object();
|
||||||
|
struct json_object *tx = json_object_new_object();
|
||||||
|
|
||||||
|
json_object_object_add(rx, "packets", json_object_new_int64(getnumber(ifname, "rx_packets")));
|
||||||
|
json_object_object_add(rx, "bytes", json_object_new_int64(getnumber(ifname, "rx_bytes")));
|
||||||
|
json_object_object_add(rx, "dropped", json_object_new_int64(getnumber(ifname, "rx_dropped")));
|
||||||
|
json_object_object_add(tx, "packets", json_object_new_int64(getnumber(ifname, "tx_packets")));
|
||||||
|
json_object_object_add(tx, "dropped", json_object_new_int64(getnumber(ifname, "tx_dropped")));
|
||||||
|
json_object_object_add(tx, "bytes", json_object_new_int64(getnumber(ifname, "tx_bytes")));
|
||||||
|
|
||||||
|
ret = json_object_new_object();
|
||||||
|
json_object_object_add(ret, "rx", rx);
|
||||||
|
json_object_object_add(ret, "tx", tx);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void count_iface_stations(size_t *wifi24, size_t *wifi5, const char *ifname) {
|
||||||
|
const struct iwinfo_ops *iw = iwinfo_backend(ifname);
|
||||||
|
if (!iw)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int freq;
|
||||||
|
if (iw->frequency(ifname, &freq) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size_t *wifi;
|
||||||
|
if (freq >= 2400 && freq < 2500)
|
||||||
|
wifi = wifi24;
|
||||||
|
else if (freq >= 5000 && freq < 6000)
|
||||||
|
wifi = wifi5;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
int len;
|
||||||
|
char buf[IWINFO_BUFSIZE];
|
||||||
|
if (iw->assoclist(ifname, buf, &len) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct iwinfo_assoclist_entry *entry;
|
||||||
|
for (entry = (struct iwinfo_assoclist_entry *)buf; (char*)(entry+1) <= buf + len; entry++)
|
||||||
|
(*wifi)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void count_stations(size_t *wifi24, size_t *wifi5) {
|
||||||
|
struct uci_context *ctx = uci_alloc_context();
|
||||||
|
ctx->flags &= ~UCI_FLAG_STRICT;
|
||||||
|
|
||||||
|
|
||||||
|
struct uci_package *p;
|
||||||
|
if (uci_load(ctx, "wireless", &p))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
|
||||||
|
struct uci_element *e;
|
||||||
|
uci_foreach_element(&p->sections, e) {
|
||||||
|
struct uci_section *s = uci_to_section(e);
|
||||||
|
if (strcmp(s->type, "wifi-iface"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const char *network = uci_lookup_option_string(ctx, s, "network");
|
||||||
|
if (!network || strcmp(network, "client"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const char *mode = uci_lookup_option_string(ctx, s, "mode");
|
||||||
|
if (!mode || strcmp(mode, "ap"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const char *ifname = uci_lookup_option_string(ctx, s, "ifname");
|
||||||
|
if (!ifname)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
count_iface_stations(wifi24, wifi5, ifname);
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
uci_free_context(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool handle_route_addgw_nexthop(char **data, void *arg) {
|
||||||
|
struct json_object *obj = (struct json_object*) arg;
|
||||||
|
if (data[PREFIX] && data[FROM] && data[VIA] && data[IF]) {
|
||||||
|
if ( (! strncmp(data[PREFIX], "::/0", 4) ) && ( ! strncmp(data[FROM], "::/0", 4) ) ) {
|
||||||
|
int gw_nexthoplen=strlen(data[VIA]) + strlen(data[IF])+2;
|
||||||
|
char gw_nexthop[gw_nexthoplen];
|
||||||
|
snprintf(gw_nexthop, gw_nexthoplen , "%s%%%s", data[VIA], data[IF]);
|
||||||
|
json_object_object_add(obj, "gateway_nexthop", json_object_new_string(gw_nexthop));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int json_parse_get_clients(json_object * object) {
|
||||||
|
if (object) {
|
||||||
|
json_object_object_foreach(object, key, val) {
|
||||||
|
if (! strncmp("clients", key, 7)) {
|
||||||
|
return(json_object_get_int(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ask_l3roamd_for_client_count() {
|
||||||
|
struct sockaddr_un addr;
|
||||||
|
const char *socket_path = "/var/run/l3roamd.sock";
|
||||||
|
int fd;
|
||||||
|
int clients = -1;
|
||||||
|
char *buf = NULL;
|
||||||
|
int already_read = 0;
|
||||||
|
|
||||||
|
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
||||||
|
fprintf(stderr, "could not setup l3roamd-control-socket\n");
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);
|
||||||
|
|
||||||
|
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
|
||||||
|
fprintf(stderr, "connect error\n");
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write(fd,"get_clients\n",12) != 12) {
|
||||||
|
perror("could not send command to l3roamd socket: get_clients");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rc = 0;
|
||||||
|
do {
|
||||||
|
buf = realloc(buf, already_read + SOCKET_INPUT_BUFFER_SIZE + 1);
|
||||||
|
if (buf == NULL) {
|
||||||
|
fprintf(stderr, "could not allocate memory for buffer\n");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = read(fd, &buf[already_read], SOCKET_INPUT_BUFFER_SIZE);
|
||||||
|
already_read+=rc;
|
||||||
|
if (rc < 0) {
|
||||||
|
perror("error on read in ask_l3roamd_for_client_count():");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
buf[already_read]='\0';
|
||||||
|
} while (rc == SOCKET_INPUT_BUFFER_SIZE);
|
||||||
|
|
||||||
|
json_object * jobj = json_tokener_parse(buf);
|
||||||
|
clients = json_parse_get_clients(jobj);
|
||||||
|
json_object_put(jobj);
|
||||||
|
|
||||||
|
end:
|
||||||
|
free(buf);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return clients;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct json_object * get_clients(void) {
|
||||||
|
size_t wifi24 = 0, wifi5 = 0;
|
||||||
|
|
||||||
|
count_stations(&wifi24, &wifi5);
|
||||||
|
|
||||||
|
size_t total = ask_l3roamd_for_client_count();
|
||||||
|
|
||||||
|
size_t wifi = wifi24 + wifi5;
|
||||||
|
struct json_object *ret = json_object_new_object();
|
||||||
|
|
||||||
|
if (total >= 0)
|
||||||
|
json_object_object_add(ret, "total", json_object_new_int(total));
|
||||||
|
|
||||||
|
json_object_object_add(ret, "wifi", json_object_new_int(wifi));
|
||||||
|
json_object_object_add(ret, "wifi24", json_object_new_int(wifi24));
|
||||||
|
json_object_object_add(ret, "wifi5", json_object_new_int(wifi5));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct json_object * respondd_provider_statistics(void) {
|
||||||
|
struct json_object *ret = json_object_new_object();
|
||||||
|
|
||||||
|
json_object_object_add(ret, "clients", get_clients());
|
||||||
|
json_object_object_add(ret, "traffic", get_traffic());
|
||||||
|
|
||||||
|
babelhelper_readbabeldata(&bhelper_ctx, (void*)ret, handle_route_addgw_nexthop );
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct json_object * get_wifi_neighbours(const char *ifname) {
|
||||||
|
const struct iwinfo_ops *iw = iwinfo_backend(ifname);
|
||||||
|
if (!iw)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
int len;
|
||||||
|
char buf[IWINFO_BUFSIZE];
|
||||||
|
if (iw->assoclist(ifname, buf, &len) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
struct json_object *neighbours = json_object_new_object();
|
||||||
|
|
||||||
|
struct iwinfo_assoclist_entry *entry;
|
||||||
|
for (entry = (struct iwinfo_assoclist_entry *)buf; (char*)(entry+1) <= buf + len; entry++) {
|
||||||
|
struct json_object *obj = json_object_new_object();
|
||||||
|
|
||||||
|
json_object_object_add(obj, "signal", json_object_new_int(entry->signal));
|
||||||
|
json_object_object_add(obj, "noise", json_object_new_int(entry->noise));
|
||||||
|
json_object_object_add(obj, "inactive", json_object_new_int(entry->inactive));
|
||||||
|
|
||||||
|
char mac[18];
|
||||||
|
snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||||
|
entry->mac[0], entry->mac[1], entry->mac[2],
|
||||||
|
entry->mac[3], entry->mac[4], entry->mac[5]);
|
||||||
|
|
||||||
|
json_object_object_add(neighbours, mac, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct json_object *ret = json_object_new_object();
|
||||||
|
|
||||||
|
if (json_object_object_length(neighbours))
|
||||||
|
json_object_object_add(ret, "neighbours", neighbours);
|
||||||
|
else
|
||||||
|
json_object_put(neighbours);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct json_object * get_wifi(void) {
|
||||||
|
|
||||||
|
struct uci_context *ctx = uci_alloc_context();
|
||||||
|
ctx->flags &= ~UCI_FLAG_STRICT;
|
||||||
|
|
||||||
|
struct json_object *ret = json_object_new_object();
|
||||||
|
|
||||||
|
struct uci_package *p;
|
||||||
|
if (uci_load(ctx, "network", &p))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
|
||||||
|
struct uci_element *e;
|
||||||
|
uci_foreach_element(&p->sections, e) {
|
||||||
|
struct uci_section *s = uci_to_section(e);
|
||||||
|
if (strcmp(s->type, "interface"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const char *proto = uci_lookup_option_string(ctx, s, "proto");
|
||||||
|
if (!proto || strcmp(proto, "gluon_mesh"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const char *ifname = uci_lookup_option_string(ctx, s, "ifname");
|
||||||
|
if (!ifname)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char *ifaddr = gluonutil_get_interface_address(ifname);
|
||||||
|
if (!ifaddr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
struct json_object *neighbours = get_wifi_neighbours(ifname);
|
||||||
|
if (neighbours)
|
||||||
|
json_object_object_add(ret, ifaddr, neighbours);
|
||||||
|
|
||||||
|
free(ifaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
uci_free_context(ctx);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct json_object * respondd_provider_neighbours(void) {
|
||||||
|
struct json_object *ret = json_object_new_object();
|
||||||
|
|
||||||
|
struct json_object *babel = get_babel_neighbours();
|
||||||
|
if (babel)
|
||||||
|
json_object_object_add(ret, "babel", babel);
|
||||||
|
|
||||||
|
|
||||||
|
struct json_object *wifi = get_wifi();
|
||||||
|
if (wifi)
|
||||||
|
json_object_object_add(ret, "wifi", wifi);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const struct respondd_provider_info respondd_providers[] = {
|
||||||
|
{"nodeinfo", respondd_provider_nodeinfo},
|
||||||
|
{"statistics", respondd_provider_statistics},
|
||||||
|
{"neighbours", respondd_provider_neighbours},
|
||||||
|
{}
|
||||||
|
};
|
28
package/gluon-mesh-babel/src/handle_neighbour.c
Normal file
28
package/gluon-mesh-babel/src/handle_neighbour.c
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "handle_neighbour.h"
|
||||||
|
#include <libbabelhelper/babelhelper.h>
|
||||||
|
|
||||||
|
bool handle_neighbour(char **data, void *arg) {
|
||||||
|
struct json_object *obj = (struct json_object*)arg;
|
||||||
|
|
||||||
|
if (data[NEIGHBOUR]) {
|
||||||
|
struct json_object *neigh = json_object_new_object();
|
||||||
|
|
||||||
|
if (data[RXCOST])
|
||||||
|
json_object_object_add(neigh, "rxcost", json_object_new_int(atoi(data[RXCOST])));
|
||||||
|
if (data[TXCOST])
|
||||||
|
json_object_object_add(neigh, "txcost", json_object_new_int(atoi(data[TXCOST])));
|
||||||
|
if (data[COST])
|
||||||
|
json_object_object_add(neigh, "cost", json_object_new_int(atoi(data[COST])));
|
||||||
|
if (data[REACH])
|
||||||
|
json_object_object_add(neigh, "reachability", json_object_new_double(strtod(data[REACH], NULL)));
|
||||||
|
if (data[IF])
|
||||||
|
json_object_object_add(neigh, "ifname", json_object_new_string(data[IF]));
|
||||||
|
if (data[ADDRESS])
|
||||||
|
json_object_object_add(obj, data[ADDRESS] , neigh);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
4
package/gluon-mesh-babel/src/handle_neighbour.h
Normal file
4
package/gluon-mesh-babel/src/handle_neighbour.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#include <json-c/json.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
bool handle_neighbour(char **line, void *arg);
|
31
package/gluon-mesh-babel/src/neighbours-babel.c
Normal file
31
package/gluon-mesh-babel/src/neighbours-babel.c
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <json-c/json.h>
|
||||||
|
#include <libbabelhelper/babelhelper.h>
|
||||||
|
#include "handle_neighbour.h"
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct json_object *neighbours;
|
||||||
|
|
||||||
|
printf("Content-type: text/event-stream\n\n");
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
struct babelhelper_ctx bhelper_ctx = {};
|
||||||
|
while (1) {
|
||||||
|
neighbours = json_object_new_object();
|
||||||
|
if (!neighbours)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bhelper_ctx.debug = false;
|
||||||
|
babelhelper_readbabeldata(&bhelper_ctx, (void*)neighbours, handle_neighbour);
|
||||||
|
|
||||||
|
printf("data: %s\n\n", json_object_to_json_string(neighbours));
|
||||||
|
fflush(stdout);
|
||||||
|
json_object_put(neighbours);
|
||||||
|
neighbours = NULL;
|
||||||
|
sleep(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user