Skip to content
Snippets Groups Projects
Commit f4126537 authored by Marcus Scharf's avatar Marcus Scharf
Browse files

rework code-struct to classes

parent c61cc69a
No related branches found
No related tags found
No related merge requests found
*.swp
*.pyc
alias.json
config.json
#!/usr/bin/env python3
# Code-Base: https://github.com/ffggrz/ffnord-alfred-announce
# + https://github.com/freifunk-mwu/ffnord-alfred-announce
# + https://github.com/FreifunkBremen/respondd
import sys
import socket
import select
import struct
import subprocess
import argparse
import re
import time
# Force encoding to UTF-8
import locale # Ensures that subsequent open()s
locale.getpreferredencoding = lambda _=None: 'UTF-8' # are UTF-8 encoded.
import json
import zlib
import netifaces as netif
def toUTF8(line):
return line.decode("utf-8")
def call(cmdnargs):
output = subprocess.check_output(cmdnargs)
lines = output.splitlines()
lines = [toUTF8(line) for line in lines]
return lines
def merge(a, b):
if isinstance(a, dict) and isinstance(b, dict):
d = dict(a)
d.update({k: merge(a.get(k, None), b[k]) for k in b})
return d
if isinstance(a, list) and isinstance(b, list):
return [merge(x, y) for x, y in itertools.izip_longest(a, b)]
return a if b is None else b
class rateLimit: # rate limit like iptables limit (per minutes)
tLast = None
def __init__(self, _rate_limit, _rate_burst):
self.rate_limit = _rate_limit
self.rate_burst = _rate_burst
self.bucket = _rate_burst
def limit(self):
tNow = time.time()
if self.tLast is None:
self.tLast = tNow
return True
tDiff = tNow - self.tLast
self.tLast = tNow
self.bucket+= (tDiff / (60 / self.rate_limit))
if self.bucket > self.rate_burst:
self.bucket = self.rate_burst
if self.bucket >= 1:
self.bucket-= 1
return True
else:
return False
def getGateway():
#/sys/kernel/debug/batman_adv/bat0/gateways
output = subprocess.check_output(["batctl", "-m", config['batman'], "gwl", "-n"])
output_utf8 = output.decode("utf-8")
lines = output_utf8.splitlines()
j = None
for line in lines:
gw_line = re.match(r"^(\*|=>) +([0-9a-f:]+) \([\d ]+\) ([0-9a-f:]+)", line)
if gw_line:
j = {}
j["gateway"] = gw_line.group(2)
j["gateway_nexthop"] = gw_line.group(3)
return j
def getClients():
#/sys/kernel/debug/batman_adv/bat0/transtable_local
output = subprocess.check_output(["batctl", "-m", config['batman'], "tl", "-n"])
output_utf8 = output.decode("utf-8")
lines = output_utf8.splitlines()
batadv_mac = getDevice_MAC(config['batman'])
j = {"total": 0, "wifi": 0}
for line in lines:
# batman-adv -> translation-table.c -> batadv_tt_local_seq_print_text
# R = BATADV_TT_CLIENT_ROAM
# P = BATADV_TT_CLIENT_NOPURGE
# N = BATADV_TT_CLIENT_NEW
# X = BATADV_TT_CLIENT_PENDING
# W = BATADV_TT_CLIENT_WIFI
# I = BATADV_TT_CLIENT_ISOLA
# . = unset
# * c0:11:73:b2:8f:dd -1 [.P..W.] 1.710 (0xe680a836)
ml = re.match(r"^\s\*\s([0-9a-f:]+)\s+-\d\s\[([RPNXWI\.]+)\]", line, re.I)
if ml:
if not batadv_mac == ml.group(1): # Filter bat0
if not ml.group(1).startswith('33:33:') and not ml.group(1).startswith('01:00:5e:'): # Filter Multicast
j["total"] += 1
if ml.group(2)[4] == 'W':
j["wifi"] += 1
return j
def getDevice_Addresses(dev):
l = []
try:
for ip6 in netif.ifaddresses(dev)[netif.AF_INET6]:
raw6 = ip6['addr'].split('%')
l.append(raw6[0])
for ip in netif.ifaddresses(dev)[netif.AF_INET]:
raw = ip['addr'].split('%')
l.append(raw[0])
except:
pass
return l
def getDevice_MAC(dev):
try:
interface = netif.ifaddresses(dev)
mac = interface[netif.AF_LINK]
return mac[0]['addr']
except:
return None
def getMesh_Interfaces():
j = {}
output = subprocess.check_output(["batctl", "-m", config['batman'], "if"])
output_utf8 = output.decode("utf-8")
lines = output_utf8.splitlines()
for line in lines:
dev_re = re.match(r"^([^:]*)", line)
dev = dev_re.group(1)
j[dev] = getDevice_MAC(dev)
return j
def getBat0_Interfaces():
j = {}
output = subprocess.check_output(["batctl", "-m", config['batman'], "if"])
output_utf8 = output.decode("utf-8")
lines = output_utf8.splitlines()
for line in lines:
dev_line = re.match(r"^([^:]*)", line)
nif = dev_line.group(0)
if_group = ""
if "fastd" in config and nif == config["fastd"]: # keep for compatibility
if_group = "tunnel"
elif nif.find("l2tp") != -1:
if_group = "l2tp"
elif "mesh-vpn" in config and nif in config["mesh-vpn"]:
if_group = "tunnel"
elif "mesh-wlan" in config and nif in config["mesh-wlan"]:
if_group = "wireless"
else:
if_group = "other"
if not if_group in j:
j[if_group] = []
j[if_group].append(getDevice_MAC(nif))
if "l2tp" in j:
if "tunnel" in j:
j["tunnel"] = j["tunnel"] + j["l2tp"]
else:
j["tunnel"] = j["l2tp"]
return j
def getTraffic():
return (lambda fields: dict(
(key, dict(
(type_, int(value_))
for key_, type_, value_ in fields
if key_ == key))
for key in ['rx', 'tx', 'forward', 'mgmt_rx', 'mgmt_tx']
))(list(
(
key.replace('_bytes', '').replace('_dropped', ''),
'bytes' if key.endswith('_bytes') else 'dropped' if key.endswith('_dropped') else 'packets',
value
)
for key, value in map(lambda s: list(map(str.strip, s.split(': ', 1))), call(['ethtool', '-S', config['batman']])[1:])
))
def getMemory():
return dict(
(key.replace('Mem', '').lower(), int(value.split(' ')[0]))
for key, value in map(lambda s: map(str.strip, s.split(': ', 1)), open('/proc/meminfo').readlines())
if key in ('MemTotal', 'MemFree', 'Buffers', 'Cached')
)
def getFastd():
fastd_data = b""
try:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(config["fastd_socket"])
except socket.error as err:
print("socket error: ", sys.stderr, err)
return None
while True:
data = sock.recv(1024)
if not data:
break
fastd_data += data
sock.close()
return json.loads(fastd_data.decode("utf-8"))
def getMeshVPNPeers():
j = {}
if "fastd_socket" in config:
fastd = getFastd()
for peer in fastd["peers"].values():
if peer["connection"]:
j[peer["name"]] = {
"established": peer["connection"]["established"]
}
else:
j[peer["name"]] = None
return j
else:
return None
from lib.respondd_client import ResponddClient
def getNode_ID():
if 'node_id' in aliases["nodeinfo"]:
return aliases["nodeinfo"]["node_id"]
else:
return getDevice_MAC(config["batman"]).replace(':', '')
def getStationDump(dev_list):
j = {}
for dev in dev_list:
try:
# iw dev ibss3 station dump
output = subprocess.check_output(["iw", "dev", dev, "station", "dump"])
output_utf8 = output.decode("utf-8")
lines = output_utf8.splitlines()
mac = ""
for line in lines:
# Station 32:b8:c3:86:3e:e8 (on ibss3)
ml = re.match(r"^Station ([0-9a-f:]+) \(on ([\w\d]+)\)", line, re.I)
if ml:
mac = ml.group(1)
j[mac] = {}
else:
ml = re.match(r"^[\t ]+([^:]+):[\t ]+([^ ]+)", line, re.I)
if ml:
j[mac][ml.group(1)] = ml.group(2)
except:
pass
return j
def getNeighbours():
# https://github.com/freifunk-gluon/packages/blob/master/net/respondd/src/respondd.c
j = {"batadv": {}}
stationDump = None
if 'mesh-wlan' in config:
j["wifi"] = {}
stationDump = getStationDump(config["mesh-wlan"])
mesh_ifs = getMesh_Interfaces()
output = subprocess.check_output(["batctl", "-m", config['batman'], "o", "-n"])
output_utf8 = output.decode("utf-8")
lines = output_utf8.splitlines()
for line in lines:
# * e2:ad:db:b7:66:63 2.712s (175) be:b7:25:4f:8f:96 [mesh-vpn-l2tp-1]
ml = re.match(r"^[ \*\t]*([0-9a-f:]+)[ ]*([\d\.]*)s[ ]*\(([ ]*\d*)\)[ ]*([0-9a-f:]+)[ ]*\[[ ]*(.*)\]", line, re.I)
if ml:
dev = ml.group(5)
mac_origin = ml.group(1)
mac_nhop = ml.group(4)
tq = ml.group(3)
lastseen = ml.group(2)
if mac_origin == mac_nhop:
if 'mesh-wlan' in config and dev in config["mesh-wlan"] and not stationDump is None:
if not mesh_ifs[dev] in j["wifi"]:
j["wifi"][mesh_ifs[dev]] = {}
j["wifi"][mesh_ifs[dev]]["neighbours"] = {}
if mac_origin in stationDump:
j["wifi"][mesh_ifs[dev]]["neighbours"][mac_origin] = {
"signal": stationDump[mac_origin]["signal"],
"noise": 0, # TODO: fehlt noch
"inactive": stationDump[mac_origin]["inactive time"]
}
if dev in mesh_ifs:
if not mesh_ifs[dev] in j["batadv"]:
j["batadv"][mesh_ifs[dev]] = {}
j["batadv"][mesh_ifs[dev]]["neighbours"] = {}
j["batadv"][mesh_ifs[dev]]["neighbours"][mac_origin] = {
"tq": int(tq),
"lastseen": float(lastseen)
}
return j
def getCPUInfo():
j = {}
with open("/proc/cpuinfo", 'r') as fh:
for line in fh:
ml = re.match(r"^(.+?)[\t ]+:[\t ]+(.*)$", line, re.I)
if ml:
j[ml.group(1)] = ml.group(2)
return j
# ======================== Output =========================
# =========================================================
def createNodeinfo():
j = {
"node_id": getNode_ID(),
"hostname": socket.gethostname(),
"network": {
"addresses": getDevice_Addresses(config['bridge']),
"mesh": {
"bat0": {
"interfaces": getBat0_Interfaces()
}
},
"mac": getDevice_MAC(config["batman"])
},
"software": {
"firmware": {
"base": call(['lsb_release', '-is'])[0],
"release": call(['lsb_release', '-ds'])[0]
},
"batman-adv": {
"version": open('/sys/module/batman_adv/version').read().strip(),
# "compat": # /lib/gluon/mesh-batman-adv-core/compat
},
"status-page": {
"api": 0
},
"autoupdater": {
"enabled": False
}
},
"hardware": {
"model": getCPUInfo()["model name"],
"nproc": int(call(['nproc'])[0])
},
# "vpn": True,
"owner": {},
"system": {},
"location": {}
}
if 'mesh-vpn' in config and len(config["mesh-vpn"]) > 0:
try:
j["software"]["fastd"] = {
"version": call(['fastd', '-v'])[0].split(' ')[1],
"enabled": True
}
except:
pass
return merge(j, aliases["nodeinfo"])
def createStatistics():
j = {
"node_id": getNode_ID(),
"clients": getClients(),
"traffic": getTraffic(),
"idletime": float(open('/proc/uptime').read().split(' ')[1]),
"loadavg": float(open('/proc/loadavg').read().split(' ')[0]),
"memory": getMemory(),
"processes": dict(zip(('running', 'total'), map(int, open('/proc/loadavg').read().split(' ')[3].split('/')))),
"uptime": float(open('/proc/uptime').read().split(' ')[0]),
"mesh_vpn" : { # HopGlass-Server: node.flags.uplink = parsePeerGroup(_.get(n, 'statistics.mesh_vpn'))
"groups": {
"backbone": {
"peers": getMeshVPNPeers()
}
}
}
}
gateway = getGateway()
if gateway != None:
j = merge(j, gateway)
return j
def createNeighbours():
#/sys/kernel/debug/batman_adv/bat0/originators
j = {
"node_id": getNode_ID()
}
j = merge(j, getNeighbours())
return j
def sendResponse(request, compress):
json_data = {}
if not RateLimit is None and not RateLimit.limit():
print("rate limit reached!")
return
#https://github.com/freifunk-gluon/packages/blob/master/net/respondd/src/respondd.c
if request == 'statistics':
json_data[request] = createStatistics()
elif request == 'nodeinfo':
json_data[request] = createNodeinfo()
elif request == 'neighbours':
json_data[request] = createNeighbours()
else:
print("unknown command: " + request)
return
json_str = bytes(json.dumps(json_data, separators=(',', ':')), 'UTF-8')
if compress:
encoder = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15) # The data may be decompressed using zlib and many zlib bindings using -15 as the window size parameter.
gzip_data = encoder.compress(json_str)
gzip_data = gzip_data + encoder.flush()
sock.sendto(gzip_data, sender)
else:
sock.sendto(json_str, sender)
if options["verbose"]:
print(json.dumps(json_data, sort_keys=True, indent=4))
# ===================== Mainfunction ======================
# =========================================================
parser = argparse.ArgumentParser()
parser.add_argument('-d', '--debug', action='store_true', help='Debug Output', required=False)
parser.add_argument('-v', '--verbose', action='store_true', help='Verbose Output', required=False)
args = parser.parse_args()
options = vars(args)
config = {}
try:
......@@ -468,58 +13,6 @@ try:
except IOError:
raise
aliases = {}
try:
with open("alias.json", 'r') as cfg_handle:
aliases = json.load(cfg_handle)
except IOError:
raise
if options["debug"]:
print(json.dumps(createNodeinfo(), sort_keys=True, indent=4))
print(json.dumps(createStatistics(), sort_keys=True, indent=4))
print(json.dumps(createNeighbours(), sort_keys=True, indent=4))
#print(json.dumps(getFastd(config["fastd_socket"]), sort_keys=True, indent=4))
#print(json.dumps(getMesh_VPN(), sort_keys=True, indent=4))
sys.exit(1)
if 'addr' in config:
addr = config['addr']
else:
addr = 'ff02::2:1001'
if 'addr' in config:
port = config['port']
else:
port = 1001
if_idx = socket.if_nametoindex(config["bridge"])
group = socket.inet_pton(socket.AF_INET6, addr) + struct.pack("I", if_idx)
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, group)
sock.bind(('::', port))
if 'rate_limit' in config:
if not 'rate_limit_burst' in config:
config['rate_limit_burst'] = 10
RateLimit = rateLimit(config['rate_limit'], config['rate_limit_burst'])
else:
RateLimit = None
# =========================================================
while True:
if select.select([sock], [], [], 1)[0]:
msg, sender = sock.recvfrom(2048)
if options["verbose"]:
print(msg)
msg_spl = str(msg, 'UTF-8').split(" ")
extResponddClient = ResponddClient(config)
extResponddClient.start()
if msg_spl[0] == 'GET': # multi_request
for req in msg_spl[1:]:
sendResponse(req, True)
else: # single_request
sendResponse(msg_spl[0], False)
#!/usr/bin/env python3
import netifaces as netif
import subprocess
def toUTF8(line):
return line.decode("utf-8")
def call(cmdnargs):
output = subprocess.check_output(cmdnargs)
lines = output.splitlines()
lines = [toUTF8(line) for line in lines]
return lines
def merge(a, b):
if isinstance(a, dict) and isinstance(b, dict):
d = dict(a)
d.update({k: merge(a.get(k, None), b[k]) for k in b})
return d
if isinstance(a, list) and isinstance(b, list):
return [merge(x, y) for x, y in itertools.izip_longest(a, b)]
return a if b is None else b
def getDevice_MAC(dev):
try:
interface = netif.ifaddresses(dev)
mac = interface[netif.AF_LINK]
return mac[0]['addr']
except:
return None
#!/usr/bin/env python3
import subprocess
import re
from lib.respondd import Respondd
import lib.helper
class Neighbours(Respondd):
def __init__(self, config):
Respondd.__init__(self, config)
def getStationDump(self, dev_list):
j = {}
for dev in dev_list:
try:
# iw dev ibss3 station dump
output = subprocess.check_output(["iw", "dev", dev, "station", "dump"])
output_utf8 = output.decode("utf-8")
lines = output_utf8.splitlines()
mac = ""
for line in lines:
# Station 32:b8:c3:86:3e:e8 (on ibss3)
ml = re.match(r"^Station ([0-9a-f:]+) \(on ([\w\d]+)\)", line, re.I)
if ml:
mac = ml.group(1)
j[mac] = {}
else:
ml = re.match(r"^[\t ]+([^:]+):[\t ]+([^ ]+)", line, re.I)
if ml:
j[mac][ml.group(1)] = ml.group(2)
except:
pass
return j
def getMesh_Interfaces(self):
j = {}
output = subprocess.check_output(["batctl", "-m", self._config['batman'], "if"])
output_utf8 = output.decode("utf-8")
lines = output_utf8.splitlines()
for line in lines:
dev_re = re.match(r"^([^:]*)", line)
dev = dev_re.group(1)
j[dev] = lib.helper.getDevice_MAC(dev)
return j
def get(self):
j = {"batadv": {}}
stationDump = None
if 'mesh-wlan' in self._config:
j["wifi"] = {}
stationDump = self.getStationDump(self._config["mesh-wlan"])
mesh_ifs = self.getMesh_Interfaces()
output = subprocess.check_output(["batctl", "-m", self._config['batman'], "o", "-n"])
output_utf8 = output.decode("utf-8")
lines = output_utf8.splitlines()
for line in lines:
# * e2:ad:db:b7:66:63 2.712s (175) be:b7:25:4f:8f:96 [mesh-vpn-l2tp-1]
ml = re.match(r"^[ \*\t]*([0-9a-f:]+)[ ]*([\d\.]*)s[ ]*\(([ ]*\d*)\)[ ]*([0-9a-f:]+)[ ]*\[[ ]*(.*)\]", line, re.I)
if ml:
dev = ml.group(5)
mac_origin = ml.group(1)
mac_nhop = ml.group(4)
tq = ml.group(3)
lastseen = ml.group(2)
if mac_origin == mac_nhop:
if 'mesh-wlan' in self._config and dev in self._config["mesh-wlan"] and not stationDump is None:
if not mesh_ifs[dev] in j["wifi"]:
j["wifi"][mesh_ifs[dev]] = {}
j["wifi"][mesh_ifs[dev]]["neighbours"] = {}
if mac_origin in stationDump:
j["wifi"][mesh_ifs[dev]]["neighbours"][mac_origin] = {
"signal": stationDump[mac_origin]["signal"],
"noise": 0, # TODO: fehlt noch
"inactive": stationDump[mac_origin]["inactive time"]
}
if dev in mesh_ifs:
if not mesh_ifs[dev] in j["batadv"]:
j["batadv"][mesh_ifs[dev]] = {}
j["batadv"][mesh_ifs[dev]]["neighbours"] = {}
j["batadv"][mesh_ifs[dev]]["neighbours"][mac_origin] = {
"tq": int(tq),
"lastseen": float(lastseen)
}
return j
#!/usr/bin/env python3
import socket
import netifaces as netif
import subprocess
import re
from lib.respondd import Respondd
import lib.helper
class Nodeinfo(Respondd):
def __init__(self, config):
Respondd.__init__(self, config)
def getDevice_Addresses(self, dev):
l = []
try:
for ip6 in netif.ifaddresses(dev)[netif.AF_INET6]:
raw6 = ip6['addr'].split('%')
l.append(raw6[0])
for ip in netif.ifaddresses(dev)[netif.AF_INET]:
raw = ip['addr'].split('%')
l.append(raw[0])
except:
pass
return l
def getBat0_Interfaces(self):
j = {}
output = subprocess.check_output(["batctl", "-m", self._config['batman'], "if"])
output_utf8 = output.decode("utf-8")
lines = output_utf8.splitlines()
for line in lines:
dev_line = re.match(r"^([^:]*)", line)
nif = dev_line.group(0)
if_group = ""
if "fastd" in self._config and nif == self._config["fastd"]: # keep for compatibility
if_group = "tunnel"
elif nif.find("l2tp") != -1:
if_group = "l2tp"
elif "mesh-vpn" in self._config and nif in self._config["mesh-vpn"]:
if_group = "tunnel"
elif "mesh-wlan" in self._config and nif in self._config["mesh-wlan"]:
if_group = "wireless"
else:
if_group = "other"
if not if_group in j:
j[if_group] = []
j[if_group].append(lib.helper.getDevice_MAC(nif))
if "l2tp" in j:
if "tunnel" in j:
j["tunnel"] = j["tunnel"] + j["l2tp"]
else:
j["tunnel"] = j["l2tp"]
return j
def getCPUInfo(self):
j = {}
with open("/proc/cpuinfo", 'r') as fh:
for line in fh:
ml = re.match(r"^(.+?)[\t ]+:[\t ]+(.*)$", line, re.I)
if ml:
j[ml.group(1)] = ml.group(2)
return j
def get(self):
j = {
"hostname": socket.gethostname(),
"network": {
"addresses": self.getDevice_Addresses(self._config['bridge']),
"mesh": {
"bat0": {
"interfaces": self.getBat0_Interfaces()
}
},
"mac": lib.helper.getDevice_MAC(self._config["batman"])
},
"software": {
"firmware": {
"base": lib.helper.call(['lsb_release', '-is'])[0],
"release": lib.helper.call(['lsb_release', '-ds'])[0]
},
"batman-adv": {
"version": open('/sys/module/batman_adv/version').read().strip(),
# "compat": # /lib/gluon/mesh-batman-adv-core/compat
},
"status-page": {
"api": 0
},
"autoupdater": {
"enabled": False
}
},
"hardware": {
"model": self.getCPUInfo()["model name"],
"nproc": int(lib.helper.call(['nproc'])[0])
},
"owner": {},
"system": {},
"location": {}
}
if 'mesh-vpn' in self._config and len(self._config["mesh-vpn"]) > 0:
try:
j["software"]["fastd"] = {
"version": lib.helper.call(['fastd', '-v'])[0].split(' ')[1],
"enabled": True
}
except:
pass
return lib.helper.merge(j, self._aliases["nodeinfo"])
#!/usr/bin/env python3
class rateLimit: # rate limit like iptables limit (per minutes)
tLast = None
def __init__(self, _rate_limit, _rate_burst):
self.rate_limit = _rate_limit
self.rate_burst = _rate_burst
self.bucket = _rate_burst
def limit(self):
tNow = time.time()
if self.tLast is None:
self.tLast = tNow
return True
tDiff = tNow - self.tLast
self.tLast = tNow
self.bucket+= (tDiff / (60 / self.rate_limit))
if self.bucket > self.rate_burst:
self.bucket = self.rate_burst
if self.bucket >= 1:
self.bucket-= 1
return True
else:
return False
#!/usr/bin/env python3
import json
import zlib
import lib.helper
class Respondd:
def __init__(self, config):
self._config = config
self._aliases = {}
try:
with open("alias.json", 'r') as cfg_handle:
self._aliases = json.load(cfg_handle)
except IOError:
raise
pass
def getNode_ID(self):
if 'node_id' in self._aliases["nodeinfo"]:
return self._aliases["nodeinfo"]["node_id"]
else:
return lib.helper.getDevice_MAC(self._config["batman"]).replace(':', '')
def getJSON(self, root=None):
print(root)
j = self.get()
j['node_id'] = self.getNode_ID()
if not root is None:
j_tmp = j
j = {}
j[root] = j_tmp
return bytes(json.dumps(j, separators=(',', ':')), 'UTF-8')
def getJSONCompressed(self, root=None):
return self.compress(self.getJSON(root))
def compress(self, data):
encoder = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION, zlib.DEFLATED, -15) # The data may be decompressed using zlib and many zlib bindings using -15 as the window size parameter.
gzip_data = encoder.compress(data)
gzip_data = gzip_data + encoder.flush()
return gzip_data
def get(self):
return {}
pass
#!/usr/bin/env python3
from lib.ratelimit import rateLimit
from lib.nodeinfo import Nodeinfo
from lib.neighbours import Neighbours
from lib.statistics import Statistics
import socket
import select
import struct
class ResponddClient:
def __init__(self, config):
self._config = config
if 'addr' in self._config:
addr = self._config['addr']
else:
addr = 'ff02::2:1001'
if 'addr' in self._config:
port = self._config['port']
else:
port = 1001
if_idx = socket.if_nametoindex(self._config["bridge"])
group = socket.inet_pton(socket.AF_INET6, addr) + struct.pack("I", if_idx)
self._sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
self._sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, group)
self._sock.bind(('::', port))
if 'rate_limit' in self._config:
if not 'rate_limit_burst' in self._config:
self._config['rate_limit_burst'] = 10
self.__RateLimit = rateLimit(self._config['rate_limit'], self._config['rate_limit_burst'])
else:
self.__RateLimit = None
self._nodeinfo = Nodeinfo(self._config)
self._neighbours = Neighbours(self._config)
self._statistics = Statistics(self._config)
def start(self):
while True:
if select.select([self._sock], [], [], 1)[0]:
msg, sender = self._sock.recvfrom(2048)
# if options["verbose"]:
# print(msg)
msg_spl = str(msg, 'UTF-8').split(" ")
if msg_spl[0] == 'GET': # multi_request
for req in msg_spl[1:]:
self.sendResponse(sender, req, True)
else: # single_request
self.sendResponse(sender, msg_spl[0], False)
def sendResponse(self, sender, request, compress):
if not self.__RateLimit is None and not self.__RateLimit.limit():
print("rate limit reached!")
return
response = None
if request == 'statistics':
response = self._statistics
elif request == 'nodeinfo':
response = self._nodeinfo
elif request == 'neighbours':
response = self._neighbours
pass
else:
print("unknown command: " + request)
return
print(response.getJSON(request))
return
if compress:
sock.sendto(response.getJSONCompressed(request), sender)
else:
sock.sendto(response.getJSON(request), sender)
# if options["verbose"]:
# print(json.dumps(json_data, sort_keys=True, indent=4))
#!/usr/bin/env python3
import socket
#import netifaces as netif
import subprocess
import re
from lib.respondd import Respondd
import lib.helper
class Statistics(Respondd):
def __init__(self, config):
Respondd.__init__(self, config)
def getClients(self):
output = subprocess.check_output(["batctl", "-m", self._config['batman'], "tl", "-n"])
output_utf8 = output.decode("utf-8")
lines = output_utf8.splitlines()
batadv_mac = lib.helper.getDevice_MAC(self._config['batman'])
j = {"total": 0, "wifi": 0}
for line in lines:
# batman-adv -> translation-table.c -> batadv_tt_local_seq_print_text
# R = BATADV_TT_CLIENT_ROAM
# P = BATADV_TT_CLIENT_NOPURGE
# N = BATADV_TT_CLIENT_NEW
# X = BATADV_TT_CLIENT_PENDING
# W = BATADV_TT_CLIENT_WIFI
# I = BATADV_TT_CLIENT_ISOLA
# . = unset
# * c0:11:73:b2:8f:dd -1 [.P..W.] 1.710 (0xe680a836)
ml = re.match(r"^\s\*\s([0-9a-f:]+)\s+-\d\s\[([RPNXWI\.]+)\]", line, re.I)
if ml:
if not batadv_mac == ml.group(1): # Filter bat0
if not ml.group(1).startswith('33:33:') and not ml.group(1).startswith('01:00:5e:'): # Filter Multicast
j["total"] += 1
if ml.group(2)[4] == 'W':
j["wifi"] += 1
return j
def getTraffic(self): # TODO: design rework needed!
return (lambda fields: dict(
(key, dict(
(type_, int(value_))
for key_, type_, value_ in fields
if key_ == key))
for key in ['rx', 'tx', 'forward', 'mgmt_rx', 'mgmt_tx']
))(list(
(
key.replace('_bytes', '').replace('_dropped', ''),
'bytes' if key.endswith('_bytes') else 'dropped' if key.endswith('_dropped') else 'packets',
value
)
for key, value in map(lambda s: list(map(str.strip, s.split(': ', 1))), lib.helper.call(['ethtool', '-S', self._config['batman']])[1:])
))
def getMemory(self): # TODO: design rework needed!
return dict(
(key.replace('Mem', '').lower(), int(value.split(' ')[0]))
for key, value in map(lambda s: map(str.strip, s.split(': ', 1)), open('/proc/meminfo').readlines())
if key in ('MemTotal', 'MemFree', 'Buffers', 'Cached')
)
def getFastd(self):
fastd_data = b""
try:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(config["fastd_socket"])
except socket.error as err:
print("socket error: ", sys.stderr, err)
return None
while True:
data = sock.recv(1024)
if not data:
break
fastd_data += data
sock.close()
return json.loads(fastd_data.decode("utf-8"))
def getMeshVPNPeers(self):
j = {}
if "fastd_socket" in self._config:
fastd = self.getFastd()
for peer in fastd["peers"].values():
if peer["connection"]:
j[peer["name"]] = {
"established": peer["connection"]["established"]
}
else:
j[peer["name"]] = None
return j
else:
return None
def getGateway(self):
output = subprocess.check_output(["batctl", "-m", self._config['batman'], "gwl", "-n"])
output_utf8 = output.decode("utf-8")
lines = output_utf8.splitlines()
j = None
for line in lines:
gw_line = re.match(r"^(\*|=>) +([0-9a-f:]+) \([\d ]+\) ([0-9a-f:]+)", line)
if gw_line:
j = {}
j["gateway"] = gw_line.group(2)
j["gateway_nexthop"] = gw_line.group(3)
return j
def get(self):
j = {
"clients": self.getClients(),
"traffic": self.getTraffic(),
"idletime": float(open('/proc/uptime').read().split(' ')[1]),
"loadavg": float(open('/proc/loadavg').read().split(' ')[0]),
"memory": self.getMemory(),
"processes": dict(zip(('running', 'total'), map(int, open('/proc/loadavg').read().split(' ')[3].split('/')))),
"uptime": float(open('/proc/uptime').read().split(' ')[0]),
"mesh_vpn" : { # HopGlass-Server: node.flags.uplink = parsePeerGroup(_.get(n, 'statistics.mesh_vpn'))
"groups": {
"backbone": {
"peers": self.getMeshVPNPeers()
}
}
}
}
gateway = self.getGateway()
if gateway != None:
j = lib.helper.merge(j, gateway)
return j
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment