Merge pull request #20007 from dhewg/prometheus-node-exporter-ucode
prometheus-node-exporter-ucode: add new package
This commit is contained in:
commit
86df457120
30 changed files with 3056 additions and 0 deletions
74
utils/prometheus-node-exporter-ucode/Makefile
Normal file
74
utils/prometheus-node-exporter-ucode/Makefile
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# Copyright (C) 2013-2017 OpenWrt.org
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=prometheus-node-exporter-ucode
|
||||||
|
PKG_VERSION:=2022.12.02
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
|
||||||
|
PKG_MAINTAINER:=Andre Heider <a.heider@gmail.com>
|
||||||
|
PKG_LICENSE:=Apache-2.0
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
Build/Compile=
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/Default
|
||||||
|
SECTION:=utils
|
||||||
|
CATEGORY:=Utilities
|
||||||
|
TITLE:=Prometheus node exporter
|
||||||
|
PKGARCH:=all
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)
|
||||||
|
$(call Package/$(PKG_NAME)/Default)
|
||||||
|
DEPENDS:=+uhttpd +uhttpd-mod-ucode +rpcd +ucode-mod-fs +ucode-mod-ubus
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/install
|
||||||
|
$(INSTALL_DIR) $(1)/etc/config
|
||||||
|
$(INSTALL_CONF) ./files/config $(1)/etc/config/$(PKG_NAME)
|
||||||
|
$(INSTALL_DIR) $(1)/etc/init.d
|
||||||
|
$(INSTALL_BIN) ./files/init $(1)/etc/init.d/$(PKG_NAME)
|
||||||
|
$(INSTALL_DIR) $(1)/usr/share/ucode/node-exporter/lib
|
||||||
|
$(INSTALL_DATA) ./files/metrics.uc $(1)/usr/share/ucode/node-exporter/
|
||||||
|
$(INSTALL_DATA) ./files/base/*.uc $(1)/usr/share/ucode/node-exporter/lib/
|
||||||
|
$(INSTALL_DIR) $(1)/usr/bin
|
||||||
|
$(INSTALL_BIN) ./files/run.sh $(1)/usr/bin/$(PKG_NAME)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/conffiles
|
||||||
|
/etc/config/$(PKG_NAME)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)/description
|
||||||
|
Provides node metrics as Prometheus scraping endpoint.
|
||||||
|
|
||||||
|
This service is a lightweight rewrite in ucode of the offical Prometheus node_exporter.
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,prometheus-node-exporter-ucode))
|
||||||
|
|
||||||
|
define Collector
|
||||||
|
define Package/$(PKG_NAME)-$(1)
|
||||||
|
$$(call Package/$(PKG_NAME)/Default)
|
||||||
|
TITLE+= ($(2))
|
||||||
|
DEPENDS:=$(PKG_NAME) $(3)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/$(PKG_NAME)-$(1)/install
|
||||||
|
$$(INSTALL_DIR) $$(1)/usr/share/ucode/node-exporter/lib
|
||||||
|
$$(INSTALL_DATA) ./files/extra/$(1).uc $$(1)/usr/share/ucode/node-exporter/lib/
|
||||||
|
endef
|
||||||
|
|
||||||
|
$$(eval $$(call BuildPackage,$(PKG_NAME)-$(1)))
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call Collector,dnsmasq,Dnsmasq collector,@dnsmasq))
|
||||||
|
$(eval $(call Collector,ltq-dsl,Lantiq/Intel/MaxLinear DSL collector,@ltq-dsl-app))
|
||||||
|
$(eval $(call Collector,netstat,netstat collector,))
|
||||||
|
$(eval $(call Collector,openwrt,OpenWrt collector,))
|
||||||
|
$(eval $(call Collector,snmp6,snmp6 collector,))
|
||||||
|
$(eval $(call Collector,uci_dhcp_host,UCI DHCP host collector,))
|
||||||
|
$(eval $(call Collector,wifi,Wi-Fi collector,+ucode-mod-nl80211))
|
||||||
|
$(eval $(call Collector,wireguard,Wireguard collector,+rpcd-mod-wireguard))
|
|
@ -0,0 +1,4 @@
|
||||||
|
gauge("node_nf_conntrack_entries")
|
||||||
|
(null, oneline("/proc/sys/net/netfilter/nf_conntrack_count"));
|
||||||
|
gauge("node_nf_conntrack_entries_limit")
|
||||||
|
(null, oneline("/proc/sys/net/netfilter/nf_conntrack_max"));
|
44
utils/prometheus-node-exporter-ucode/files/base/cpu.uc
Normal file
44
utils/prometheus-node-exporter-ucode/files/base/cpu.uc
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
let f = fs.open("/proc/stat");
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const desc = [
|
||||||
|
null,
|
||||||
|
"user",
|
||||||
|
"nice",
|
||||||
|
"system",
|
||||||
|
"idle",
|
||||||
|
"iowait",
|
||||||
|
"irq",
|
||||||
|
"softirq",
|
||||||
|
"steal",
|
||||||
|
"guest",
|
||||||
|
"guest_nice",
|
||||||
|
];
|
||||||
|
const m_cpu = counter("node_cpu_seconds_total");
|
||||||
|
|
||||||
|
let line;
|
||||||
|
while (line = nextline(f)) {
|
||||||
|
const x = wsplit(line);
|
||||||
|
|
||||||
|
if (length(x) < 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (match(x[0], /^cpu\d+/)) {
|
||||||
|
const count = min(length(x), length(desc));
|
||||||
|
for (let i = 1; i < count; i++)
|
||||||
|
m_cpu({ cpu: x[0], mode: desc[i] }, x[i] / 100.0);
|
||||||
|
} else if (x[0] == "intr")
|
||||||
|
counter("node_intr_total")(null, x[1]);
|
||||||
|
else if (x[0] == "ctxt")
|
||||||
|
counter("node_context_switches_total")(null, x[1]);
|
||||||
|
else if (x[0] == "btime")
|
||||||
|
gauge("node_boot_time_seconds")(null, x[1]);
|
||||||
|
else if (x[0] == "processes")
|
||||||
|
counter("node_forks_total")(null, x[1]);
|
||||||
|
else if (x[0] == "procs_running")
|
||||||
|
gauge("node_procs_running_total")(null, x[1]);
|
||||||
|
else if (x[0] == "procs_blocked")
|
||||||
|
gauge("node_procs_blocked_total")(null, x[1]);
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
gauge("node_entropy_available_bits")
|
||||||
|
(null, oneline("/proc/sys/kernel/random/entropy_avail"));
|
||||||
|
gauge("node_entropy_pool_size_bits")
|
||||||
|
(null, oneline("/proc/sys/kernel/random/poolsize"));
|
|
@ -0,0 +1,7 @@
|
||||||
|
const x = wsplit(oneline("/proc/sys/fs/file-nr"));
|
||||||
|
|
||||||
|
if (length(x) < 3)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
gauge("node_filefd_allocated")(null, x[0]);
|
||||||
|
gauge("node_filefd_maximum")(null, x[2]);
|
|
@ -0,0 +1,8 @@
|
||||||
|
const x = wsplit(oneline("/proc/loadavg"));
|
||||||
|
|
||||||
|
if (length(x) < 3)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
gauge("node_load1")(null, x[0]);
|
||||||
|
gauge("node_load5")(null, x[1]);
|
||||||
|
gauge("node_load15")(null, x[2]);
|
24
utils/prometheus-node-exporter-ucode/files/base/meminfo.uc
Normal file
24
utils/prometheus-node-exporter-ucode/files/base/meminfo.uc
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
let f = fs.open("/proc/meminfo");
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let line;
|
||||||
|
while (line = nextline(f)) {
|
||||||
|
const x = wsplit(line);
|
||||||
|
|
||||||
|
if (length(x) < 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (substr(x[0], -1) != ":")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let name;
|
||||||
|
if (substr(x[0], -2) == "):")
|
||||||
|
name = replace(substr(x[0], 0, -2), "(", "_");
|
||||||
|
else
|
||||||
|
name = substr(x[0], 0, -1);
|
||||||
|
|
||||||
|
gauge(`node_memory_${name}_bytes`)
|
||||||
|
(null, x[2] == "kB" ? x[1] * 1024 : x[1]);
|
||||||
|
}
|
48
utils/prometheus-node-exporter-ucode/files/base/netclass.uc
Normal file
48
utils/prometheus-node-exporter-ucode/files/base/netclass.uc
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
const root = "/sys/class/net/";
|
||||||
|
const devices = fs.lsdir(root);
|
||||||
|
|
||||||
|
if (length(devices) < 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const m_info = gauge("node_network_info");
|
||||||
|
const m_speed = gauge("node_network_speed_bytes");
|
||||||
|
const metrics = {
|
||||||
|
addr_assign_type: gauge("node_network_address_assign_type"),
|
||||||
|
carrier: gauge("node_network_carrier"),
|
||||||
|
carrier_changes: counter("node_network_carrier_changes_total"),
|
||||||
|
carrier_down_count: counter("node_network_carrier_down_changes_total"),
|
||||||
|
carrier_up_count: counter("node_network_carrier_up_changes_total"),
|
||||||
|
dev_id: gauge("node_network_device_id"),
|
||||||
|
dormant: gauge("node_network_dormant"),
|
||||||
|
flags: gauge("node_network_flags"),
|
||||||
|
ifindex: gauge("node_network_iface_id"),
|
||||||
|
iflink: gauge("node_network_iface_link"),
|
||||||
|
link_mode: gauge("node_network_iface_link_mode"),
|
||||||
|
mtu: gauge("node_network_mtu_bytes"),
|
||||||
|
name_assign_type: gauge("node_network_name_assign_type"),
|
||||||
|
netdev_group: gauge("node_network_net_dev_group"),
|
||||||
|
type: gauge("node_network_protocol_type"),
|
||||||
|
tx_queue_len: gauge("node_network_transmit_queue_length"),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let device in devices) {
|
||||||
|
const devroot = root + device + "/";
|
||||||
|
|
||||||
|
m_info({
|
||||||
|
device,
|
||||||
|
address: oneline(devroot + "address"),
|
||||||
|
broadcast: oneline(devroot + "broadcast"),
|
||||||
|
duplex: oneline(devroot + "duplex"),
|
||||||
|
operstate: oneline(devroot + "operstate"),
|
||||||
|
ifalias: oneline(devroot + "ifalias"),
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
for (let m in metrics) {
|
||||||
|
let line = oneline(devroot + m);
|
||||||
|
metrics[m]({ device }, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
const speed = int(oneline(devroot + "speed"));
|
||||||
|
if (speed > 0)
|
||||||
|
m_speed({ device }, speed * 1000 * 1000 / 8);
|
||||||
|
}
|
40
utils/prometheus-node-exporter-ucode/files/base/netdev.uc
Normal file
40
utils/prometheus-node-exporter-ucode/files/base/netdev.uc
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
let f = fs.open("/proc/net/dev");
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const m = [
|
||||||
|
null,
|
||||||
|
counter("node_network_receive_bytes_total"),
|
||||||
|
counter("node_network_receive_packets_total"),
|
||||||
|
counter("node_network_receive_errs_total"),
|
||||||
|
counter("node_network_receive_drop_total"),
|
||||||
|
counter("node_network_receive_fifo_total"),
|
||||||
|
counter("node_network_receive_frame_total"),
|
||||||
|
counter("node_network_receive_compressed_total"),
|
||||||
|
counter("node_network_receive_multicast_total"),
|
||||||
|
counter("node_network_transmit_bytes_total"),
|
||||||
|
counter("node_network_transmit_packets_total"),
|
||||||
|
counter("node_network_transmit_errs_total"),
|
||||||
|
counter("node_network_transmit_drop_total"),
|
||||||
|
counter("node_network_transmit_fifo_total"),
|
||||||
|
counter("node_network_transmit_colls_total"),
|
||||||
|
counter("node_network_transmit_carrier_total"),
|
||||||
|
counter("node_network_transmit_compressed_total"),
|
||||||
|
];
|
||||||
|
|
||||||
|
let line;
|
||||||
|
while (line = nextline(f)) {
|
||||||
|
const x = wsplit(ltrim(line), " ");
|
||||||
|
|
||||||
|
if (length(x) < 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (substr(x[0], -1) != ":")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const count = min(length(x), length(m));
|
||||||
|
const labels = { device: substr(x[0], 0, -1) };
|
||||||
|
for (let i = 1; i < count; i++)
|
||||||
|
m[i](labels, x[i]);
|
||||||
|
}
|
10
utils/prometheus-node-exporter-ucode/files/base/selinux.uc
Normal file
10
utils/prometheus-node-exporter-ucode/files/base/selinux.uc
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
const mode = oneline("/sys/fs/selinux/enforce");
|
||||||
|
const enabled = gauge("node_selinux_enabled");
|
||||||
|
|
||||||
|
if (mode == null) {
|
||||||
|
enabled(null, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled(null, 1);
|
||||||
|
gauge("node_selinux_current_mode")(null, mode);
|
1
utils/prometheus-node-exporter-ucode/files/base/time.uc
Normal file
1
utils/prometheus-node-exporter-ucode/files/base/time.uc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
gauge("node_time_seconds")(null, time());
|
8
utils/prometheus-node-exporter-ucode/files/base/uname.uc
Normal file
8
utils/prometheus-node-exporter-ucode/files/base/uname.uc
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
gauge("node_uname_info")({
|
||||||
|
sysname: oneline("/proc/sys/kernel/ostype"),
|
||||||
|
nodename: oneline("/proc/sys/kernel/hostname"),
|
||||||
|
release: oneline("/proc/sys/kernel/osrelease"),
|
||||||
|
version: oneline("/proc/sys/kernel/version"),
|
||||||
|
machine: poneline("uname -m"), // TODO lame
|
||||||
|
domainname: oneline("/proc/sys/kernel/domainname"),
|
||||||
|
}, 1);
|
7
utils/prometheus-node-exporter-ucode/files/config
Normal file
7
utils/prometheus-node-exporter-ucode/files/config
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
config prometheus-node-exporter-ucode 'main'
|
||||||
|
option listen_interface 'loopback'
|
||||||
|
option listen_port '9101'
|
||||||
|
option http_keepalive '70'
|
||||||
|
|
||||||
|
config collector 'wifi'
|
||||||
|
option stations '1'
|
|
@ -0,0 +1,6 @@
|
||||||
|
const x = ubus.call("dnsmasq", "metrics");
|
||||||
|
if (!x)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (let i in x)
|
||||||
|
gauge(`dnsmasq_${i}_total`)(null, x[i]);
|
72
utils/prometheus-node-exporter-ucode/files/extra/ltq-dsl.uc
Normal file
72
utils/prometheus-node-exporter-ucode/files/extra/ltq-dsl.uc
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
const x = ubus.call("dsl", "metrics");
|
||||||
|
|
||||||
|
if (!x)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
gauge("dsl_info")({
|
||||||
|
atuc_vendor: x.atu_c.vendor,
|
||||||
|
atuc_system_vendor: x.atu_c.system_vendor,
|
||||||
|
chipset: x.chipset,
|
||||||
|
firmware_version: x.firmware_version,
|
||||||
|
api_version: x.api_version,
|
||||||
|
driver_version: x.driver_version,
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
gauge("dsl_line_info")({
|
||||||
|
annex: x.annex,
|
||||||
|
standard: x.standard,
|
||||||
|
mode: x.mode,
|
||||||
|
profile: x.profile,
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
gauge("dsl_up")({ detail: x.state }, x.up);
|
||||||
|
gauge("dsl_uptime_seconds")(null, x.uptime);
|
||||||
|
|
||||||
|
gauge("dsl_line_attenuation_db")
|
||||||
|
({ direction: "down" }, x.downstream.latn)
|
||||||
|
({ direction: "up" }, x.upstream.latn);
|
||||||
|
gauge("dsl_signal_attenuation_db")
|
||||||
|
({ direction: "down" }, x.downstream.satn)
|
||||||
|
({ direction: "up" }, x.upstream.satn);
|
||||||
|
gauge("dsl_signal_to_noise_margin_db")
|
||||||
|
({ direction: "down" }, x.downstream.snr)
|
||||||
|
({ direction: "up" }, x.upstream.snr);
|
||||||
|
gauge("dsl_aggregated_transmit_power_db")
|
||||||
|
({ direction: "down" }, x.downstream.actatp)
|
||||||
|
({ direction: "up" }, x.upstream.actatp);
|
||||||
|
|
||||||
|
if (x.downstream.interleave_delay)
|
||||||
|
gauge("dsl_latency_seconds")
|
||||||
|
({ direction: "down" }, x.downstream.interleave_delay / 1000000.0)
|
||||||
|
({ direction: "up" }, x.upstream.interleave_delay / 1000000.0);
|
||||||
|
gauge("dsl_datarate")
|
||||||
|
({ direction: "down" }, x.downstream.data_rate)
|
||||||
|
({ direction: "up" }, x.upstream.data_rate);
|
||||||
|
gauge("dsl_max_datarate")
|
||||||
|
({ direction: "down" }, x.downstream.attndr)
|
||||||
|
({ direction: "up" }, x.upstream.attndr);
|
||||||
|
|
||||||
|
counter("dsl_error_seconds_total")
|
||||||
|
({ err: "forward error correction", loc: "near" }, x.errors.near.fecs)
|
||||||
|
({ err: "forward error correction", loc: "far" }, x.errors.far.fecs)
|
||||||
|
({ err: "errored", loc: "near" }, x.errors.near.es)
|
||||||
|
({ err: "errored", loc: "far" }, x.errors.far.es)
|
||||||
|
({ err: "severely errored", loc: "near" }, x.errors.near.ses)
|
||||||
|
({ err: "severely errored", loc: "far" }, x.errors.far.ses)
|
||||||
|
({ err: "loss of signal", loc: "near" }, x.errors.near.loss)
|
||||||
|
({ err: "loss of signal", loc: "far" }, x.errors.far.loss)
|
||||||
|
({ err: "unavailable", loc: "near" }, x.errors.near.uas)
|
||||||
|
({ err: "unavailable", loc: "far" }, x.errors.far.uas);
|
||||||
|
|
||||||
|
counter("dsl_errors_total")
|
||||||
|
({ err: "header error code error", loc: "near" }, x.errors.near.hec)
|
||||||
|
({ err: "header error code error", loc: "far" }, x.errors.far.hec)
|
||||||
|
({ err: "non pre-emptive crc error", loc: "near" }, x.errors.near.crc_p)
|
||||||
|
({ err: "non pre-emptive crc error", loc: "far" }, x.errors.far.crc_p)
|
||||||
|
({ err: "pre-emptive crc error", loc: "near" }, x.errors.near.crcp_p)
|
||||||
|
({ err: "pre-emptive crc error", loc: "far" }, x.errors.far.crcp_p);
|
||||||
|
|
||||||
|
if (x.erb)
|
||||||
|
counter("dsl_erb_total")
|
||||||
|
({ counter: "sent" }, x.erb.sent)
|
||||||
|
({ counter: "discarded" }, x.erb.discarded);
|
30
utils/prometheus-node-exporter-ucode/files/extra/netstat.uc
Normal file
30
utils/prometheus-node-exporter-ucode/files/extra/netstat.uc
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
function parse(fn) {
|
||||||
|
let f = fs.open(fn);
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
let names, values;
|
||||||
|
while (names = nextline(f), values = nextline(f)) {
|
||||||
|
const name = wsplit(names);
|
||||||
|
const value = wsplit(values);
|
||||||
|
|
||||||
|
if (name[0] != value[0])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (length(name) != length(value))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let prefix = substr(name[0], 0, -1);
|
||||||
|
for (let i = 1; i < length(name); i++)
|
||||||
|
gauge(`node_netstat_${prefix}_${name[i]}`)(null, value[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let n = parse("/proc/net/netstat");
|
||||||
|
let s = parse("/proc/net/snmp");
|
||||||
|
|
||||||
|
if (!n && !s)
|
||||||
|
return false;
|
14
utils/prometheus-node-exporter-ucode/files/extra/openwrt.uc
Normal file
14
utils/prometheus-node-exporter-ucode/files/extra/openwrt.uc
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
const x = ubus.call("system", "board");
|
||||||
|
|
||||||
|
if (!x)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
gauge("node_openwrt_info")({
|
||||||
|
board_name: x.board_name,
|
||||||
|
id: x.release.distribution,
|
||||||
|
model: x.model,
|
||||||
|
release: x.release.version,
|
||||||
|
revision: x.release.revision,
|
||||||
|
system: x.system,
|
||||||
|
target: x.release.target,
|
||||||
|
}, 1);
|
23
utils/prometheus-node-exporter-ucode/files/extra/snmp6.uc
Normal file
23
utils/prometheus-node-exporter-ucode/files/extra/snmp6.uc
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
function parse(fn, device, skipdecl) {
|
||||||
|
let f = fs.open(fn);
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const labels = { device };
|
||||||
|
let line;
|
||||||
|
while (line = nextline(f)) {
|
||||||
|
const x = wsplit(line);
|
||||||
|
|
||||||
|
if (length(x) < 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
counter(`snmp6_${x[0]}`, null, skipdecl)(labels, x[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parse("/proc/net/snmp6", "all");
|
||||||
|
|
||||||
|
const root = "/proc/net/dev_snmp6/";
|
||||||
|
for (let device in fs.lsdir(root))
|
||||||
|
parse(root + device, device, true);
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { cursor } from "uci";
|
||||||
|
|
||||||
|
const uci = cursor();
|
||||||
|
uci.load("dhcp");
|
||||||
|
|
||||||
|
let m = gauge("dhcp_host_info");
|
||||||
|
|
||||||
|
uci.foreach('dhcp', `host`, (s) => {
|
||||||
|
m({
|
||||||
|
name: s.name,
|
||||||
|
mac: s.mac,
|
||||||
|
ip: s.ip,
|
||||||
|
}, 1);
|
||||||
|
});
|
118
utils/prometheus-node-exporter-ucode/files/extra/wifi.uc
Normal file
118
utils/prometheus-node-exporter-ucode/files/extra/wifi.uc
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
import { request, 'const' as wlconst } from 'nl80211';
|
||||||
|
|
||||||
|
const x = ubus.call("network.wireless", "status");
|
||||||
|
|
||||||
|
if (!x)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const iftypes = [
|
||||||
|
"Unknown",
|
||||||
|
"Ad-Hoc",
|
||||||
|
"Client",
|
||||||
|
"Master",
|
||||||
|
"Master (VLAN)",
|
||||||
|
"WDS",
|
||||||
|
"Monitor",
|
||||||
|
"Mesh Point",
|
||||||
|
"P2P Client",
|
||||||
|
"P2P Go",
|
||||||
|
"P2P Device",
|
||||||
|
"OCB",
|
||||||
|
];
|
||||||
|
|
||||||
|
let m_radio_info = gauge("wifi_radio_info");
|
||||||
|
let m_network_info = gauge("wifi_network_info");
|
||||||
|
let m_network_quality = gauge("wifi_network_quality");
|
||||||
|
let m_network_bitrate = gauge("wifi_network_bitrate");
|
||||||
|
let m_network_noise = gauge("wifi_network_noise_dbm");
|
||||||
|
let m_network_signal = gauge("wifi_network_signal_dbm");
|
||||||
|
let m_stations_total = counter("wifi_stations_total");
|
||||||
|
let m_station_inactive = gauge("wifi_station_inactive_milliseconds");
|
||||||
|
let m_station_rx_bytes = counter("wifi_station_receive_bytes_total");
|
||||||
|
let m_station_tx_bytes = counter("wifi_station_transmit_bytes_total");
|
||||||
|
let m_station_rx_packets = counter("wifi_station_receive_packets_total");
|
||||||
|
let m_station_tx_packets = counter("wifi_station_transmit_packets_total");
|
||||||
|
let m_station_signal = gauge("wifi_station_signal_dbm");
|
||||||
|
let m_station_rx_bitrate = gauge("wifi_station_receive_kilobits_per_second");
|
||||||
|
let m_station_tx_bitrate = gauge("wifi_station_transmit_kilobits_per_second");
|
||||||
|
let m_station_exp_tp = gauge("wifi_station_expected_throughput_kilobits_per_second");
|
||||||
|
|
||||||
|
for (let radio in x) {
|
||||||
|
const rc = x[radio]["config"];
|
||||||
|
|
||||||
|
m_radio_info({
|
||||||
|
radio,
|
||||||
|
htmode: rc["htmode"],
|
||||||
|
channel: rc["channel"],
|
||||||
|
country: rc["country"],
|
||||||
|
} ,1);
|
||||||
|
|
||||||
|
for (let iface in x[radio]["interfaces"]) {
|
||||||
|
const ifname = iface["ifname"];
|
||||||
|
const nc = iface["config"];
|
||||||
|
const wif = request(wlconst.NL80211_CMD_GET_INTERFACE, 0, { dev: ifname });
|
||||||
|
|
||||||
|
if (!wif)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
m_network_info({
|
||||||
|
radio,
|
||||||
|
ifname,
|
||||||
|
ssid: nc["ssid"] || nc["mesh_id"],
|
||||||
|
bssid: wif["mac"],
|
||||||
|
mode: iftypes[wif["iftype"]],
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
const wsta = request(wlconst.NL80211_CMD_GET_STATION, wlconst.NLM_F_DUMP, { dev: ifname });
|
||||||
|
let signal = 0;
|
||||||
|
let bitrate = 0;
|
||||||
|
const stations = length(wsta) || 0;
|
||||||
|
if (stations) {
|
||||||
|
for (let sta in wsta) {
|
||||||
|
signal += sta["sta_info"].signal;
|
||||||
|
bitrate += sta["sta_info"]["tx_bitrate"].bitrate32;
|
||||||
|
}
|
||||||
|
bitrate /= stations * 0.01;
|
||||||
|
signal /= stations;
|
||||||
|
}
|
||||||
|
|
||||||
|
let labels = { radio, ifname };
|
||||||
|
m_network_bitrate(labels, bitrate || NaN);
|
||||||
|
m_network_signal(labels, signal || NaN);
|
||||||
|
m_network_quality(labels, signal ? 100.0 / 70 * (signal + 110) : NaN);
|
||||||
|
|
||||||
|
const wsur = request(wlconst.NL80211_CMD_GET_SURVEY, wlconst.NLM_F_DUMP, { dev: ifname });
|
||||||
|
let noise = 0;
|
||||||
|
for (let i in wsur) {
|
||||||
|
if (i["survey_info"]["frequency"] != wif["wiphy_freq"])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
noise = i["survey_info"]["noise"];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_network_noise(labels, noise || NaN);
|
||||||
|
|
||||||
|
if (config["stations"] != "1")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
m_stations_total(labels, stations);
|
||||||
|
if (!stations)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (let sta in wsta) {
|
||||||
|
labels["mac"] = sta["mac"];
|
||||||
|
const info = sta["sta_info"];
|
||||||
|
|
||||||
|
m_station_inactive(labels, info["inactive_time"]);
|
||||||
|
m_station_rx_bytes(labels, info["rx_bytes64"]);
|
||||||
|
m_station_tx_bytes(labels, info["tx_bytes64"]);
|
||||||
|
m_station_rx_packets(labels, info["rx_packets"]);
|
||||||
|
m_station_tx_packets(labels, info["tx_packets"]);
|
||||||
|
m_station_signal(labels, info["signal"]);
|
||||||
|
m_station_rx_bitrate(labels, info["rx_bitrate"]["bitrate32"] * 100);
|
||||||
|
m_station_tx_bitrate(labels, info["tx_bitrate"]["bitrate32"] * 100);
|
||||||
|
m_station_exp_tp(labels, info["expected_throughput"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { cursor } from "uci";
|
||||||
|
|
||||||
|
const x = ubus.call("wireguard", "status");
|
||||||
|
if (!x)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const uci = cursor();
|
||||||
|
uci.load("network");
|
||||||
|
|
||||||
|
let m_wg_iface_info = gauge("wireguard_interface_info");
|
||||||
|
let m_wg_peer_info = gauge("wireguard_peer_info");
|
||||||
|
let m_wg_handshake = gauge ("wireguard_latest_handshake_seconds");
|
||||||
|
let m_wg_rx = gauge ("wireguard_received_bytes_total");
|
||||||
|
let m_wg_tx = gauge ("wireguard_sent_bytes_total");
|
||||||
|
|
||||||
|
for (let iface in x) {
|
||||||
|
const wc = x[iface];
|
||||||
|
|
||||||
|
m_wg_iface_info({
|
||||||
|
name: iface,
|
||||||
|
public_key: wc["public_key"],
|
||||||
|
listen_port: wc["listen_port"],
|
||||||
|
fwmark: wc["fwmark"] || NaN,
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
for (let peer in wc["peers"]) {
|
||||||
|
let description;
|
||||||
|
uci.foreach('network', `wireguard_${iface}`, (s) => {
|
||||||
|
if (s.public_key == peer)
|
||||||
|
description = s.description;
|
||||||
|
});
|
||||||
|
|
||||||
|
const pc = wc["peers"][peer];
|
||||||
|
|
||||||
|
m_wg_peer_info({
|
||||||
|
interface: iface,
|
||||||
|
public_key: peer,
|
||||||
|
description,
|
||||||
|
endpoint: pc["endpoint"],
|
||||||
|
persistent_keepalive_interval: pc["persistent_keepalive_interval"] || NaN,
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
const labels = { public_key: peer };
|
||||||
|
|
||||||
|
m_wg_handshake(labels, pc["last_handshake"]);
|
||||||
|
m_wg_rx(labels, pc["rx_bytes"]);
|
||||||
|
m_wg_tx(labels, pc["tx_bytes"]);
|
||||||
|
}
|
||||||
|
}
|
73
utils/prometheus-node-exporter-ucode/files/init
Normal file
73
utils/prometheus-node-exporter-ucode/files/init
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
# Copyright (C) 2013-2017 OpenWrt.org
|
||||||
|
|
||||||
|
START=60
|
||||||
|
USE_PROCD=1
|
||||||
|
|
||||||
|
_log() {
|
||||||
|
logger -p daemon.info -t prometheus-node-exporter-ucode "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
. /lib/functions/network.sh
|
||||||
|
|
||||||
|
local interface port bind4 bind6
|
||||||
|
|
||||||
|
config_load prometheus-node-exporter-ucode.main
|
||||||
|
config_get interface "main" listen_interface "loopback"
|
||||||
|
config_get port "main" listen_port 9101
|
||||||
|
config_get keepalive "main" http_keepalive 70
|
||||||
|
|
||||||
|
[ "$interface" = "*" ] || {
|
||||||
|
network_get_ipaddr bind4 "$interface"
|
||||||
|
network_get_ipaddr6 bind6 "$interface"
|
||||||
|
[ -n "$bind4$bind6" ] || {
|
||||||
|
_log "defering start until listen interface $interface becomes ready"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
procd_open_instance
|
||||||
|
|
||||||
|
procd_set_param command /usr/sbin/uhttpd -f -c /dev/null -h /dev/null -S -D -o /metrics -O /usr/share/ucode/node-exporter/metrics.uc
|
||||||
|
|
||||||
|
if [ "$interface" = "*" ]; then
|
||||||
|
procd_append_param command -p $port
|
||||||
|
else
|
||||||
|
[ -n "$bind4" ] && procd_append_param command -p $bind4:$port
|
||||||
|
[ -n "$bind6" ] && procd_append_param command -p [$bind6]:$port
|
||||||
|
fi
|
||||||
|
[ $keepalive -gt 0 ] && procd_append_param command -k $keepalive
|
||||||
|
|
||||||
|
procd_add_jail prometheus-node-exporter-ucode log procfs sysfs ubus
|
||||||
|
procd_add_jail_mount "/usr/lib/uhttpd_ucode.so"
|
||||||
|
procd_add_jail_mount "/lib/libubus.so*"
|
||||||
|
procd_add_jail_mount "/lib/libuci.so"
|
||||||
|
procd_add_jail_mount "/usr/lib/ucode"
|
||||||
|
procd_add_jail_mount "/usr/lib/libnl*.so*"
|
||||||
|
procd_add_jail_mount "/usr/share/ucode/node-exporter"
|
||||||
|
procd_add_jail_mount "/etc/config"
|
||||||
|
|
||||||
|
# TODO breaks the dsl collector?
|
||||||
|
#procd_set_param user nobody
|
||||||
|
#procd_set_param group nogroup
|
||||||
|
procd_set_param no_new_privs 1
|
||||||
|
|
||||||
|
procd_set_param stdout 1
|
||||||
|
procd_set_param stderr 1
|
||||||
|
procd_set_param respawn
|
||||||
|
|
||||||
|
procd_close_instance
|
||||||
|
}
|
||||||
|
|
||||||
|
service_triggers()
|
||||||
|
{
|
||||||
|
local interface
|
||||||
|
|
||||||
|
procd_add_reload_trigger "prometheus-node-exporter-ucode"
|
||||||
|
|
||||||
|
config_load prometheus-node-exporter-ucode.main
|
||||||
|
config_get interface "main" listen_interface "loopback"
|
||||||
|
|
||||||
|
[ "$interface" = "*" ] || procd_add_reload_interface_trigger "$interface"
|
||||||
|
}
|
227
utils/prometheus-node-exporter-ucode/files/metrics.uc
Normal file
227
utils/prometheus-node-exporter-ucode/files/metrics.uc
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
{%
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { connect } from "ubus";
|
||||||
|
import { cursor } from "uci";
|
||||||
|
|
||||||
|
function debug(...s) {
|
||||||
|
if (global.debug)
|
||||||
|
warn("DEBUG: ", ...s, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function puts(...s) {
|
||||||
|
return uhttpd.send(...s, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function govalue(value) {
|
||||||
|
if (value == Infinity)
|
||||||
|
return "+Inf";
|
||||||
|
else if (value == -Infinity)
|
||||||
|
return "-Inf";
|
||||||
|
else if (value != value)
|
||||||
|
return "NaN";
|
||||||
|
else if (type(value) in [ "int", "double" ])
|
||||||
|
return value;
|
||||||
|
else if (type(value) in [ "bool", "string" ])
|
||||||
|
return +value;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function metric(name, mtype, help, skipdecl) {
|
||||||
|
let func;
|
||||||
|
let decl = skipdecl == true ? false : true;
|
||||||
|
|
||||||
|
let yield = function(labels, value) {
|
||||||
|
let v = govalue(value);
|
||||||
|
|
||||||
|
if (v == null) {
|
||||||
|
debug(`skipping metric: unsupported value '${value}' (${name})`);
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
|
let labels_str = "";
|
||||||
|
if (length(labels)) {
|
||||||
|
let sep = "";
|
||||||
|
let s;
|
||||||
|
labels_str = "{";
|
||||||
|
for (let l in labels) {
|
||||||
|
if (labels[l] == null)
|
||||||
|
s = "";
|
||||||
|
else if (type(labels[l]) == "string") {
|
||||||
|
s = labels[l];
|
||||||
|
s = replace(labels[l], "\\", "\\\\");
|
||||||
|
s = replace(s, "\"", "\\\"");
|
||||||
|
s = replace(s, "\n", "\\n");
|
||||||
|
} else {
|
||||||
|
s = govalue(labels[l]);
|
||||||
|
|
||||||
|
if (!s)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_str += sep + l + "=\"" + s + "\"";
|
||||||
|
sep = ",";
|
||||||
|
}
|
||||||
|
labels_str += "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decl) {
|
||||||
|
if (help)
|
||||||
|
puts("# HELP ", name, " ", help);
|
||||||
|
puts("# TYPE ", name, " ", mtype);
|
||||||
|
decl = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
puts(name, labels_str, " ", v);
|
||||||
|
return func;
|
||||||
|
};
|
||||||
|
|
||||||
|
func = yield;
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
|
function counter(name, help, skipdecl) {
|
||||||
|
return metric(name, "counter", help, skipdecl);
|
||||||
|
}
|
||||||
|
|
||||||
|
function gauge(name, help, skipdecl) {
|
||||||
|
return metric(name, "gauge", help, skipdecl);
|
||||||
|
}
|
||||||
|
|
||||||
|
function httpstatus(status) {
|
||||||
|
puts("Status: ", status, "\nContent-Type: text/plain; version=0.0.4; charset=utf-8\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function clockdiff(t1, t2) {
|
||||||
|
return (t2[0] - t1[0]) * 1000000000 + t2[1] - t1[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
let collectors = {};
|
||||||
|
|
||||||
|
global.handle_request = function(env) {
|
||||||
|
let scope = {
|
||||||
|
config: null,
|
||||||
|
fs,
|
||||||
|
ubus: connect(),
|
||||||
|
counter,
|
||||||
|
gauge,
|
||||||
|
wsplit: function(line) {
|
||||||
|
return split(line, /\s+/);
|
||||||
|
},
|
||||||
|
nextline: function(f) {
|
||||||
|
return rtrim(f.read("line"), "\n");
|
||||||
|
},
|
||||||
|
oneline: function(fn) {
|
||||||
|
let f = fs.open(fn);
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return nextline(f);
|
||||||
|
},
|
||||||
|
poneline: function(cmd) {
|
||||||
|
let f = fs.popen(cmd);
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return nextline(f);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (length(collectors) < 1) {
|
||||||
|
httpstatus("404 No Collectors found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cols = [];
|
||||||
|
for (let q in split(env.QUERY_STRING, "&")) {
|
||||||
|
let s = split(q, "=", 2);
|
||||||
|
if (length(s) == 2 && s[0] == "collect") {
|
||||||
|
if (!(s[1] in collectors)) {
|
||||||
|
httpstatus(`404 Collector ${s[1]} not found`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
push(cols, s[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length(cols) > 0)
|
||||||
|
cols = uniq(cols);
|
||||||
|
else
|
||||||
|
cols = keys(collectors);
|
||||||
|
|
||||||
|
httpstatus("200 OK");
|
||||||
|
|
||||||
|
let duration = gauge("node_scrape_collector_duration_seconds");
|
||||||
|
let success = gauge("node_scrape_collector_success");
|
||||||
|
|
||||||
|
for (let col in cols) {
|
||||||
|
let ok = false;
|
||||||
|
let t1, t2;
|
||||||
|
|
||||||
|
scope["config"] = collectors[col].config;
|
||||||
|
t1 = clock(true);
|
||||||
|
try {
|
||||||
|
ok = call(collectors[col].func, null, scope) != false;
|
||||||
|
} catch(e) {
|
||||||
|
warn(`error running collector '${col}':\n${e.message}\n`);
|
||||||
|
}
|
||||||
|
t2 = clock(true);
|
||||||
|
|
||||||
|
duration({ collector: col }, clockdiff(t1, t2) / 1000000000.0);
|
||||||
|
success({ collector: col }, ok);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const lib = "/usr/share/ucode/node-exporter/lib";
|
||||||
|
const opts = {
|
||||||
|
strict_declarations: true,
|
||||||
|
raw_mode: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let cols = fs.lsdir(lib, "*.uc");
|
||||||
|
for (let col in cols) {
|
||||||
|
let func;
|
||||||
|
let uci = cursor();
|
||||||
|
|
||||||
|
try {
|
||||||
|
func = loadfile(lib + "/" + col, opts);
|
||||||
|
} catch(e) {
|
||||||
|
warn(`error compiling collector '${col}':\n${e.message}\n`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = substr(col, 0, -3);
|
||||||
|
let config = uci.get_all("prometheus-node-exporter-ucode", name);
|
||||||
|
if (!config || config[".type"] != "collector")
|
||||||
|
config = {};
|
||||||
|
else {
|
||||||
|
delete config[".anonymous"];
|
||||||
|
delete config[".type"];
|
||||||
|
delete config[".name"];
|
||||||
|
}
|
||||||
|
|
||||||
|
collectors[name] = {
|
||||||
|
func,
|
||||||
|
config,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
warn(`prometheus-node-exporter-ucode now serving requests with ${length(collectors)} collectors\n`);
|
||||||
|
|
||||||
|
if (!("uhttpd" in global)) {
|
||||||
|
global.debug = true;
|
||||||
|
|
||||||
|
puts = function(...s) {
|
||||||
|
return print(...s, "\n");
|
||||||
|
};
|
||||||
|
|
||||||
|
handle_request({
|
||||||
|
QUERY_STRING: join("&", map(ARGV, v => "collect=" + v)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
%}
|
2
utils/prometheus-node-exporter-ucode/files/run.sh
Executable file
2
utils/prometheus-node-exporter-ucode/files/run.sh
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/sh
|
||||||
|
exec /usr/bin/ucode -T /usr/share/ucode/node-exporter/metrics.uc $*
|
3
utils/prometheus-node-exporter-ucode/test.sh
Executable file
3
utils/prometheus-node-exporter-ucode/test.sh
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
prometheus-node-exporter-ucode time
|
31
utils/rpcd-mod-wireguard/Makefile
Normal file
31
utils/rpcd-mod-wireguard/Makefile
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=rpcd-mod-wireguard
|
||||||
|
PKG_RELEASE=1
|
||||||
|
|
||||||
|
PKG_LICENSE:=LGPL-2.1+
|
||||||
|
PKG_BUILD_FLAGS:=gc-sections
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
include $(INCLUDE_DIR)/cmake.mk
|
||||||
|
|
||||||
|
define Package/rpcd-mod-wireguard
|
||||||
|
SECTION:=libs
|
||||||
|
CATEGORY:=Libraries
|
||||||
|
TITLE:=WireGuard rpcd module
|
||||||
|
DEPENDS:=+rpcd +kmod-wireguard
|
||||||
|
MAINTAINER:=Andre Heider <a.heider@gmail.com>
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/rpcd-mod-wireguard/install
|
||||||
|
$(INSTALL_DIR) $(1)/usr/lib/rpcd
|
||||||
|
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/lib/rpcd/wireguard.so \
|
||||||
|
$(1)/usr/lib/rpcd/
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/rpcd-mod-wireguard/postinst
|
||||||
|
#!/bin/sh
|
||||||
|
[ -n "$$IPKG_INSTROOT" ] || /etc/init.d/rpcd reload
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,rpcd-mod-wireguard))
|
10
utils/rpcd-mod-wireguard/src/CMakeLists.txt
Normal file
10
utils/rpcd-mod-wireguard/src/CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
cmake_minimum_required(VERSION 2.8.12)
|
||||||
|
PROJECT(rpcd-mod-wireguard)
|
||||||
|
ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations)
|
||||||
|
|
||||||
|
SET(SOURCES wireguard.c api.c)
|
||||||
|
|
||||||
|
ADD_LIBRARY(rpcd-mod-wireguard SHARED ${SOURCES})
|
||||||
|
|
||||||
|
SET_TARGET_PROPERTIES(rpcd-mod-wireguard PROPERTIES OUTPUT_NAME wireguard PREFIX "")
|
||||||
|
INSTALL(TARGETS rpcd-mod-wireguard LIBRARY DESTINATION lib/rpcd)
|
245
utils/rpcd-mod-wireguard/src/api.c
Normal file
245
utils/rpcd-mod-wireguard/src/api.c
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-2.1+
|
||||||
|
// Copyright (C) 2023 Andre Heider <a.heider@gmail.com>
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <libubox/blobmsg.h>
|
||||||
|
#include <libubox/blobmsg_json.h>
|
||||||
|
|
||||||
|
#include <libubus.h>
|
||||||
|
|
||||||
|
#include <rpcd/plugin.h>
|
||||||
|
|
||||||
|
#include "wireguard.h"
|
||||||
|
|
||||||
|
static struct blob_buf buf;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
RPC_PK_DEVICE,
|
||||||
|
__RPC_PK_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct blobmsg_policy rpc_privatekey_policy[__RPC_PK_MAX] = {
|
||||||
|
[RPC_PK_DEVICE] = { .name = "private", .type = BLOBMSG_TYPE_STRING },
|
||||||
|
};
|
||||||
|
|
||||||
|
static void rpc_wireguard_add_endpoint(const wg_endpoint *endpoint)
|
||||||
|
{
|
||||||
|
char host[1025]; // NI_MAXHOST
|
||||||
|
char serv[32]; // NI_MAXSERV
|
||||||
|
char res[sizeof(host) + sizeof(serv) + 4];
|
||||||
|
socklen_t addr_len;
|
||||||
|
|
||||||
|
memset(res, 0, sizeof(res));
|
||||||
|
if (endpoint->addr.sa_family == AF_INET)
|
||||||
|
addr_len = sizeof(struct sockaddr_in);
|
||||||
|
else if (endpoint->addr.sa_family == AF_INET6)
|
||||||
|
addr_len = sizeof(struct sockaddr_in6);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (getnameinfo(&endpoint->addr, addr_len, host, sizeof(host), serv, sizeof(serv),
|
||||||
|
NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (endpoint->addr.sa_family == AF_INET6 && strchr(host, ':'))
|
||||||
|
snprintf(res, sizeof(res), "[%s]:%s", host, serv);
|
||||||
|
else
|
||||||
|
snprintf(res, sizeof(res), "%s:%s", host, serv);
|
||||||
|
res[sizeof(res) - 1] = 0;
|
||||||
|
|
||||||
|
blobmsg_add_string(&buf, "endpoint", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_wireguard_add_allowedip(const wg_allowedip *allowedip)
|
||||||
|
{
|
||||||
|
char res[INET6_ADDRSTRLEN + 4 + 1];
|
||||||
|
|
||||||
|
memset(res, 0, sizeof(res));
|
||||||
|
if (allowedip->family == AF_INET)
|
||||||
|
inet_ntop(AF_INET, &allowedip->ip4, res, INET6_ADDRSTRLEN);
|
||||||
|
else if (allowedip->family == AF_INET6)
|
||||||
|
inet_ntop(AF_INET6, &allowedip->ip6, res, INET6_ADDRSTRLEN);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!res[0])
|
||||||
|
return;
|
||||||
|
|
||||||
|
sprintf(res + strlen(res), "/%u", allowedip->cidr);
|
||||||
|
res[sizeof(res) - 1] = 0;
|
||||||
|
|
||||||
|
blobmsg_add_string(&buf, NULL, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_wireguard_add_peer(const wg_peer *peer)
|
||||||
|
{
|
||||||
|
void *c;
|
||||||
|
struct wg_allowedip *allowedip;
|
||||||
|
|
||||||
|
rpc_wireguard_add_endpoint(&peer->endpoint);
|
||||||
|
|
||||||
|
c = blobmsg_open_array(&buf, "allowed_ips");
|
||||||
|
wg_for_each_allowedip(peer, allowedip)
|
||||||
|
rpc_wireguard_add_allowedip(allowedip);
|
||||||
|
blobmsg_close_array(&buf, c);
|
||||||
|
|
||||||
|
blobmsg_add_u64(&buf, "last_handshake", peer->last_handshake_time.tv_sec);
|
||||||
|
blobmsg_add_u64(&buf, "rx_bytes", peer->rx_bytes);
|
||||||
|
blobmsg_add_u64(&buf, "tx_bytes", peer->tx_bytes);
|
||||||
|
if (peer->persistent_keepalive_interval)
|
||||||
|
blobmsg_add_u16(&buf, "persistent_keepalive_interval", peer->persistent_keepalive_interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_wireguard_add_device(const wg_device *device)
|
||||||
|
{
|
||||||
|
void *c, *d;
|
||||||
|
wg_peer *peer;
|
||||||
|
wg_key_b64_string key;
|
||||||
|
|
||||||
|
blobmsg_add_u32(&buf, "ifindex", device->ifindex);
|
||||||
|
|
||||||
|
if (device->flags & WGDEVICE_HAS_PUBLIC_KEY) {
|
||||||
|
wg_key_to_base64(key, device->public_key);
|
||||||
|
blobmsg_add_string(&buf, "public_key", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->listen_port)
|
||||||
|
blobmsg_add_u16(&buf, "listen_port", device->listen_port);
|
||||||
|
|
||||||
|
if (device->fwmark)
|
||||||
|
blobmsg_add_u32(&buf, "fwmark", device->fwmark);
|
||||||
|
|
||||||
|
c = blobmsg_open_table(&buf, "peers");
|
||||||
|
wg_for_each_peer(device, peer) {
|
||||||
|
wg_key_to_base64(key, peer->public_key);
|
||||||
|
d = blobmsg_open_table(&buf, key);
|
||||||
|
rpc_wireguard_add_peer(peer);
|
||||||
|
blobmsg_close_table(&buf, d);
|
||||||
|
}
|
||||||
|
blobmsg_close_table(&buf, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rpc_wireguard_status(struct ubus_context *ctx, struct ubus_object *obj,
|
||||||
|
struct ubus_request_data *req, const char *method, struct blob_attr *msg)
|
||||||
|
{
|
||||||
|
void *c;
|
||||||
|
char *device_names, *device_name;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
device_names = wg_list_device_names();
|
||||||
|
if (!device_names)
|
||||||
|
return UBUS_STATUS_NOT_FOUND;
|
||||||
|
|
||||||
|
blob_buf_init(&buf, 0);
|
||||||
|
|
||||||
|
wg_for_each_device_name(device_names, device_name, len) {
|
||||||
|
wg_device *device;
|
||||||
|
|
||||||
|
if (wg_get_device(&device, device_name) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
c = blobmsg_open_table(&buf, device_name);
|
||||||
|
rpc_wireguard_add_device(device);
|
||||||
|
blobmsg_close_table(&buf, c);
|
||||||
|
|
||||||
|
wg_free_device(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(device_names);
|
||||||
|
|
||||||
|
ubus_send_reply(ctx, req, buf.head);
|
||||||
|
|
||||||
|
return UBUS_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rpc_wireguard_genkey(struct ubus_context *ctx, struct ubus_object *obj,
|
||||||
|
struct ubus_request_data *req, const char *method, struct blob_attr *msg)
|
||||||
|
{
|
||||||
|
wg_key private_key, public_key;
|
||||||
|
wg_key_b64_string key;
|
||||||
|
|
||||||
|
wg_generate_private_key(private_key);
|
||||||
|
wg_generate_public_key(public_key, private_key);
|
||||||
|
|
||||||
|
blob_buf_init(&buf, 0);
|
||||||
|
wg_key_to_base64(key, private_key);
|
||||||
|
blobmsg_add_string(&buf, "private", key);
|
||||||
|
wg_key_to_base64(key, public_key);
|
||||||
|
blobmsg_add_string(&buf, "public", key);
|
||||||
|
ubus_send_reply(ctx, req, buf.head);
|
||||||
|
|
||||||
|
return UBUS_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rpc_wireguard_genpsk(struct ubus_context *ctx, struct ubus_object *obj,
|
||||||
|
struct ubus_request_data *req, const char *method, struct blob_attr *msg)
|
||||||
|
{
|
||||||
|
wg_key preshared_key;
|
||||||
|
wg_key_b64_string key;
|
||||||
|
|
||||||
|
wg_generate_preshared_key(preshared_key);
|
||||||
|
|
||||||
|
blob_buf_init(&buf, 0);
|
||||||
|
wg_key_to_base64(key, preshared_key);
|
||||||
|
blobmsg_add_string(&buf, "preshared", key);
|
||||||
|
ubus_send_reply(ctx, req, buf.head);
|
||||||
|
|
||||||
|
return UBUS_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rpc_wireguard_pubkey(struct ubus_context *ctx, struct ubus_object *obj,
|
||||||
|
struct ubus_request_data *req, const char *method, struct blob_attr *msg)
|
||||||
|
{
|
||||||
|
static struct blob_attr *tb[__RPC_PK_MAX];
|
||||||
|
wg_key_b64_string key;
|
||||||
|
wg_key private_key, public_key;
|
||||||
|
|
||||||
|
blobmsg_parse(rpc_privatekey_policy, __RPC_PK_MAX, tb, blob_data(msg), blob_len(msg));
|
||||||
|
|
||||||
|
if (!tb[RPC_PK_DEVICE])
|
||||||
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||||
|
|
||||||
|
if (wg_key_from_base64(private_key, blobmsg_get_string(tb[RPC_PK_DEVICE])))
|
||||||
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||||
|
|
||||||
|
wg_generate_public_key(public_key, private_key);
|
||||||
|
blob_buf_init(&buf, 0);
|
||||||
|
wg_key_to_base64(key, public_key);
|
||||||
|
blobmsg_add_string(&buf, "public", key);
|
||||||
|
ubus_send_reply(ctx, req, buf.head);
|
||||||
|
|
||||||
|
return UBUS_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rpc_wireguard_api_init(const struct rpc_daemon_ops *ops, struct ubus_context *ctx)
|
||||||
|
{
|
||||||
|
static const struct ubus_method wireguard_methods[] = {
|
||||||
|
UBUS_METHOD_NOARG("status", rpc_wireguard_status),
|
||||||
|
UBUS_METHOD_NOARG("genkey", rpc_wireguard_genkey),
|
||||||
|
UBUS_METHOD_NOARG("genpsk", rpc_wireguard_genpsk),
|
||||||
|
UBUS_METHOD("pubkey", rpc_wireguard_pubkey, rpc_privatekey_policy),
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct ubus_object_type wireguard_type =
|
||||||
|
UBUS_OBJECT_TYPE("rpcd-plugin-wireguard", wireguard_methods);
|
||||||
|
|
||||||
|
static struct ubus_object obj = {
|
||||||
|
.name = "wireguard",
|
||||||
|
.type = &wireguard_type,
|
||||||
|
.methods = wireguard_methods,
|
||||||
|
.n_methods = ARRAY_SIZE(wireguard_methods),
|
||||||
|
};
|
||||||
|
|
||||||
|
return ubus_add_object(ctx, &obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct rpc_plugin rpc_plugin = {
|
||||||
|
.init = rpc_wireguard_api_init
|
||||||
|
};
|
1755
utils/rpcd-mod-wireguard/src/wireguard.c
Normal file
1755
utils/rpcd-mod-wireguard/src/wireguard.c
Normal file
File diff suppressed because it is too large
Load diff
105
utils/rpcd-mod-wireguard/src/wireguard.h
Normal file
105
utils/rpcd-mod-wireguard/src/wireguard.h
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WIREGUARD_H
|
||||||
|
#define WIREGUARD_H
|
||||||
|
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef uint8_t wg_key[32];
|
||||||
|
typedef char wg_key_b64_string[((sizeof(wg_key) + 2) / 3) * 4 + 1];
|
||||||
|
|
||||||
|
/* Cross platform __kernel_timespec */
|
||||||
|
struct timespec64 {
|
||||||
|
int64_t tv_sec;
|
||||||
|
int64_t tv_nsec;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct wg_allowedip {
|
||||||
|
uint16_t family;
|
||||||
|
union {
|
||||||
|
struct in_addr ip4;
|
||||||
|
struct in6_addr ip6;
|
||||||
|
};
|
||||||
|
uint8_t cidr;
|
||||||
|
struct wg_allowedip *next_allowedip;
|
||||||
|
} wg_allowedip;
|
||||||
|
|
||||||
|
enum wg_peer_flags {
|
||||||
|
WGPEER_REMOVE_ME = 1U << 0,
|
||||||
|
WGPEER_REPLACE_ALLOWEDIPS = 1U << 1,
|
||||||
|
WGPEER_HAS_PUBLIC_KEY = 1U << 2,
|
||||||
|
WGPEER_HAS_PRESHARED_KEY = 1U << 3,
|
||||||
|
WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef union wg_endpoint {
|
||||||
|
struct sockaddr addr;
|
||||||
|
struct sockaddr_in addr4;
|
||||||
|
struct sockaddr_in6 addr6;
|
||||||
|
} wg_endpoint;
|
||||||
|
|
||||||
|
typedef struct wg_peer {
|
||||||
|
enum wg_peer_flags flags;
|
||||||
|
|
||||||
|
wg_key public_key;
|
||||||
|
wg_key preshared_key;
|
||||||
|
|
||||||
|
wg_endpoint endpoint;
|
||||||
|
|
||||||
|
struct timespec64 last_handshake_time;
|
||||||
|
uint64_t rx_bytes, tx_bytes;
|
||||||
|
uint16_t persistent_keepalive_interval;
|
||||||
|
|
||||||
|
struct wg_allowedip *first_allowedip, *last_allowedip;
|
||||||
|
struct wg_peer *next_peer;
|
||||||
|
} wg_peer;
|
||||||
|
|
||||||
|
enum wg_device_flags {
|
||||||
|
WGDEVICE_REPLACE_PEERS = 1U << 0,
|
||||||
|
WGDEVICE_HAS_PRIVATE_KEY = 1U << 1,
|
||||||
|
WGDEVICE_HAS_PUBLIC_KEY = 1U << 2,
|
||||||
|
WGDEVICE_HAS_LISTEN_PORT = 1U << 3,
|
||||||
|
WGDEVICE_HAS_FWMARK = 1U << 4
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct wg_device {
|
||||||
|
char name[IFNAMSIZ];
|
||||||
|
uint32_t ifindex;
|
||||||
|
|
||||||
|
enum wg_device_flags flags;
|
||||||
|
|
||||||
|
wg_key public_key;
|
||||||
|
wg_key private_key;
|
||||||
|
|
||||||
|
uint32_t fwmark;
|
||||||
|
uint16_t listen_port;
|
||||||
|
|
||||||
|
struct wg_peer *first_peer, *last_peer;
|
||||||
|
} wg_device;
|
||||||
|
|
||||||
|
#define wg_for_each_device_name(__names, __name, __len) for ((__name) = (__names), (__len) = 0; ((__len) = strlen(__name)); (__name) += (__len) + 1)
|
||||||
|
#define wg_for_each_peer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer)
|
||||||
|
#define wg_for_each_allowedip(__peer, __allowedip) for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); (__allowedip) = (__allowedip)->next_allowedip)
|
||||||
|
|
||||||
|
int wg_set_device(wg_device *dev);
|
||||||
|
int wg_get_device(wg_device **dev, const char *device_name);
|
||||||
|
int wg_add_device(const char *device_name);
|
||||||
|
int wg_del_device(const char *device_name);
|
||||||
|
void wg_free_device(wg_device *dev);
|
||||||
|
char *wg_list_device_names(void); /* first\0second\0third\0forth\0last\0\0 */
|
||||||
|
void wg_key_to_base64(wg_key_b64_string base64, const wg_key key);
|
||||||
|
int wg_key_from_base64(wg_key key, const wg_key_b64_string base64);
|
||||||
|
bool wg_key_is_zero(const wg_key key);
|
||||||
|
void wg_generate_public_key(wg_key public_key, const wg_key private_key);
|
||||||
|
void wg_generate_private_key(wg_key private_key);
|
||||||
|
void wg_generate_preshared_key(wg_key preshared_key);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue