summaryrefslogtreecommitdiff
path: root/roles/install_respondd_poller/files
diff options
context:
space:
mode:
Diffstat (limited to 'roles/install_respondd_poller/files')
-rw-r--r--roles/install_respondd_poller/files/requirements.txt2
-rw-r--r--roles/install_respondd_poller/files/respondd_poller.py147
-rw-r--r--roles/install_respondd_poller/files/respondd_poller.service12
3 files changed, 161 insertions, 0 deletions
diff --git a/roles/install_respondd_poller/files/requirements.txt b/roles/install_respondd_poller/files/requirements.txt
new file mode 100644
index 0000000..83bb832
--- /dev/null
+++ b/roles/install_respondd_poller/files/requirements.txt
@@ -0,0 +1,2 @@
+wgnlpy
+requests
diff --git a/roles/install_respondd_poller/files/respondd_poller.py b/roles/install_respondd_poller/files/respondd_poller.py
new file mode 100644
index 0000000..1eb98a1
--- /dev/null
+++ b/roles/install_respondd_poller/files/respondd_poller.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python
+
+import socket
+import ipaddress
+import threading
+import time
+import zlib
+import json
+import os.path
+import sys
+from wgnlpy import WireGuard
+import requests
+from xml.etree import ElementTree
+
+if not os.path.exists("/etc/respondd_poller.json"):
+ print("/etc/respondd_poller.json missing")
+ sys.exit(1)
+
+interface = None
+prefix = None
+yanic_addr = None
+request = None
+
+with open("/etc/respondd_poller.json", "r") as f:
+ config = json.load(f)
+ if "interface" in config:
+ interface = config["interface"]
+ if "prefix" in config:
+ prefix = ipaddress.IPv6Network(config["prefix"])
+ if "yanic_addr" in config and "yanic_port" in config:
+ yanic_addr = (config["yanic_addr"], int(config["yanic_port"]))
+ if "request" in config:
+ request = config["request"].encode("ascii")
+
+wg = WireGuard()
+sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
+last_request = dict()
+last_response = dict()
+
+def get_wg_peers():
+ wgpeers = wg.get_interface(interface).peers
+ for peer in wgpeers:
+ for ip in wgpeers[peer].allowedips:
+ if ip.subnet_of(prefix):
+ yield ip
+
+def inflate(data):
+ decompress = zlib.decompressobj(-zlib.MAX_WBITS)
+ inflated = decompress.decompress(data)
+ inflated += decompress.flush()
+ return inflated.decode()
+
+def cleanup():
+ while True:
+ time.sleep(60)
+ old = time.monotonic() - 360
+ ips = []
+ macs = []
+ for ip in last_request:
+ if last_response[ip] < old:
+ ips.append(ip)
+ for ip in ips:
+ del last_response[ip]
+ del last_request[ip]
+
+def recv():
+ global sock
+ while True:
+ data, addr = sock.recvfrom(1500)
+ sock.sendto(data, yanic_addr)
+ j = json.loads(inflate(data))
+ last_response[ipaddress.IPv6Address(addr[0])] = time.monotonic()
+
+def send(ip):
+ global request
+ try:
+ sock.sendto(request, (bytearray(str(ip).encode('ascii')), 1001))
+ except:
+ print("failed to send packet to", ip)
+ return
+
+def get_http_nodeinfo(ip):
+ global last_request
+ now = time.monotonic()
+ try:
+ status = requests.get('http://[' + str(ip) + ']/cgi-bin/status')
+ except:
+ return
+ status_tree = ElementTree.fromstring(status.content)
+ mesh_ifs = []
+ interface_list = status_tree.findall(".//*[@data-interface]")
+ for interface in interface_list:
+ mesh_ifs.append(interface.attrib["data-interface"])
+ for mesh_if in mesh_ifs:
+ try:
+ nodeinfo = requests.get('http://[' + str(ip) + ']/cgi-bin/dyn/neighbours-nodeinfo?' + mesh_if)
+ except:
+ return
+ for line in nodeinfo.content.split(b'\n'):
+ if line.startswith(b'data: {'):
+ data = line.split(b': ', maxsplit=1)[1]
+ data = json.loads(data)
+ if "network" in data and "addresses" in data["network"]:
+ for address in data["network"]["addresses"]:
+ if ipaddress.IPv6Network(address).subnet_of(prefix):
+ node_ip = ipaddress.IPv6Address(address)
+ if node_ip not in last_request:
+ last_request[node_ip] = now
+ last_response[node_ip] = now
+
+def scan_wg_peers():
+ global last_request
+ while True:
+ print("scanning wg peers")
+ request_threads = []
+ now = time.monotonic()
+ for net in get_wg_peers():
+ ip = ipaddress.IPv6Address(str(net.network_address) + "1")
+ if ip not in last_request:
+ last_request[ip] = now
+ last_response[ip] = now
+ request_thread = threading.Thread(target=get_http_nodeinfo, args=(ip,))
+ request_thread.start()
+ request_threads.append(request_thread)
+ if len(request_threads) > 10:
+ for thread in request_threads:
+ thread.join()
+ request_threads = []
+ time.sleep(60)
+
+
+listen_thread = threading.Thread(target=recv)
+listen_thread.start()
+cleanup_thread = threading.Thread(target=cleanup)
+cleanup_thread.start()
+scan_thread = threading.Thread(target=scan_wg_peers)
+scan_thread.start()
+
+last_wg_time = 0
+
+while True:
+ now = time.monotonic()
+ for ip in last_request:
+ if now - last_request[ip] > 15:
+ last_request[ip] = now
+ send(ip)
+ time.sleep(1)
diff --git a/roles/install_respondd_poller/files/respondd_poller.service b/roles/install_respondd_poller/files/respondd_poller.service
new file mode 100644
index 0000000..96e309c
--- /dev/null
+++ b/roles/install_respondd_poller/files/respondd_poller.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=respondd_poller
+After=network.target
+
+[Service]
+ExecStart=/opt/respondd_poller/venv/bin/python -u /opt/respondd_poller/respondd_poller.py
+Restart=always
+WorkingDirectory=/opt/respondd_poller
+Environment=PYTHONPATH=/opt/respondd_poller
+
+[Install]
+WantedBy=multi-user.target