+++ /dev/null
-#!/bin/sh
-
-. /lib/functions.sh
-. /lib/functions/network.sh
-. /usr/share/libubox/jshn.sh
-. /lib/mwan3/common.sh
-
-report_connected_v4() {
- local address
-
- if [ -n "$($IPT4 -S mwan3_connected_ipv4 2> /dev/null)" ]; then
- for address in $($IPS -o save list mwan3_connected_ipv4 | grep add | cut -d " " -f 3); do
- json_add_string "" "${address}"
- done
- fi
-}
-
-report_connected_v6() {
- [ $NO_IPV6 -ne 0 ] && return
- local address
-
- if [ -n "$($IPT6 -S mwan3_connected_ipv6 2> /dev/null)" ]; then
- for address in $($IPS -o save list mwan3_connected_ipv6 | grep add | cut -d " " -f 3); do
- json_add_string "" "${address}"
- done
- fi
-}
-
-report_policies() {
- local ipt="$1"
- local policy="$2"
-
- local percent total_weight weight iface
-
- total_weight=$($ipt -S $policy | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | head -1 | awk '{print $3}')
-
- for iface in $($ipt -S $policy | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | awk '{print $1}'); do
- weight=$($ipt -S $policy | grep -v '.*--comment "out .*" .*$' | cut -s -d'"' -f2 | awk '$1 == "'$iface'"' | awk '{print $2}')
- percent=$(($weight*100/$total_weight))
- json_add_object
- json_add_string interface "$iface"
- json_add_int percent "$percent"
- json_close_object
- done
-}
-
-report_policies_v4() {
- local policy
-
- for policy in $($IPT4 -S | awk '{print $2}' | grep mwan3_policy_ | sort -u); do
- json_add_array "${policy##*mwan3_policy_}"
- report_policies "$IPT4" "$policy"
- json_close_array
- done
-}
-
-report_policies_v6() {
- [ $NO_IPV6 -ne 0 ] && return
- local policy
-
- for policy in $($IPT6 -S | awk '{print $2}' | grep mwan3_policy_ | sort -u); do
- json_add_array "${policy##*mwan3_policy_}"
- report_policies "$IPT6" "$policy"
- json_close_array
- done
-}
-
-get_age() {
- local time_p time_u
- iface="$2"
- readfile time_p "$MWAN3TRACK_STATUS_DIR/${iface}/TIME"
- [ -z "${time_p}" ] || {
- get_uptime time_n
- export -n "$1=$((time_n-time_p))"
- }
-}
-
-get_offline_time() {
- local time_n time_d iface
- iface="$2"
- readfile time_d "$MWAN3TRACK_STATUS_DIR/${iface}/OFFLINE"
- [ -z "${time_d}" ] || [ "${time_d}" = "0" ] || {
- get_uptime time_n
- export -n "$1=$((time_n-time_d))"
- }
-}
-
-get_mwan3_status() {
- local iface="${1}"
- local iface_select="${2}"
- local running="0"
- local age=0
- local online=0
- local offline=0
- local enabled time_p time_n time_u time_d status track_status up uptime temp
-
- if [ "${iface}" != "${iface_select}" ] && [ "${iface_select}" != "" ]; then
- return
- fi
-
- mwan3_get_mwan3track_status track_status "$1"
- [ "$track_status" = "active" ] && running="1"
- get_age age "$iface"
- get_online_time online "$iface"
- get_offline_time offline "$iface"
-
- config_get_bool enabled "$iface" enabled 0
-
- if [ -f "$MWAN3TRACK_STATUS_DIR/${iface}/STATUS" ]; then
- network_get_uptime uptime "$iface"
- [ -n "$uptime" ] && up=1 || up=0
- readfile status "$MWAN3TRACK_STATUS_DIR/${iface}/STATUS"
- else
- uptime=0
- up=0
- status="unknown"
- fi
-
- json_add_object "${iface}"
- json_add_int age "$age"
- json_add_int online "${online}"
- json_add_int offline "${offline}"
- json_add_int uptime "${uptime}"
- readfile temp "$MWAN3TRACK_STATUS_DIR/${iface}/SCORE"
- json_add_int "score" "$temp"
- readfile temp "$MWAN3TRACK_STATUS_DIR/${iface}/LOST"
- json_add_int "lost" "$temp"
- readfile temp "$MWAN3TRACK_STATUS_DIR/${iface}/TURN"
- json_add_int "turn" "$temp"
- json_add_string "status" "${status}"
- json_add_boolean "enabled" "${enabled}"
- json_add_boolean "running" "${running}"
- json_add_string "tracking" "${track_status}"
- json_add_boolean "up" "${up}"
- json_add_array "track_ip"
- for file in $MWAN3TRACK_STATUS_DIR/${iface}/TRACK_*; do
- [ -z "${file#*/TRACK_OUTPUT}" ] && continue
- [ -z "${file#*/TRACK_\*}" ] && continue
- track="${file#*/TRACK_}"
- json_add_object
- json_add_string ip "${track}"
- readfile temp "${file}"
- json_add_string status "$temp"
- readfile temp "$MWAN3TRACK_STATUS_DIR/${iface}/LATENCY_${track}"
- json_add_int latency "$temp"
- readfile temp "$MWAN3TRACK_STATUS_DIR/${iface}/LOSS_${track}"
- json_add_int packetloss "$temp"
- json_close_object
- done
- json_close_array
- json_close_object
-}
-
-main () {
-
- case "$1" in
- list)
- json_init
- json_add_object "status"
- json_add_string "section" "x"
- json_add_string "interface" "x"
- json_add_string "policies" "x"
- json_close_object
- json_dump
- ;;
- call)
- case "$2" in
- status)
- local section iface
- read input;
- json_load "$input"
- json_get_var section section
- json_get_var iface interface
-
- config_load mwan3
- json_init
- case "$section" in
- interfaces)
- json_add_object interfaces
- config_foreach get_mwan3_status interface "${iface}"
- json_close_object
- ;;
- connected)
- json_add_object connected
- json_add_array ipv4
- report_connected_v4
- json_close_array
- json_add_array ipv6
- report_connected_v6
- json_close_array
- json_close_object
- ;;
- policies)
- json_add_object policies
- json_add_object ipv4
- report_policies_v4
- json_close_object
- json_add_object ipv6
- report_policies_v6
- json_close_object
- json_close_object
- ;;
- *)
- # interfaces
- json_add_object interfaces
- config_foreach get_mwan3_status interface
- json_close_object
- # connected
- json_add_object connected
- json_add_array ipv4
- report_connected_v4
- json_close_array
- json_add_array ipv6
- report_connected_v6
- json_close_array
- json_close_object
- # policies
- json_add_object policies
- json_add_object ipv4
- report_policies_v4
- json_close_object
- json_add_object ipv6
- report_policies_v6
- json_close_object
- json_close_object
- ;;
- esac
- json_dump
- ;;
- esac
- ;;
- esac
-}
-
-main "$@"
--- /dev/null
+'use strict';
+
+import { popen, readfile } from 'fs';
+import { cursor } from 'uci';
+
+const ubus = require('ubus').connect();
+
+function get_str_raw(iface, property) {
+ return readfile(sprintf('/var/run/mwan3track/%s/%s', iface, property));
+}
+
+function get_str(iface, property) {
+ return rtrim(get_str_raw(iface, property), '\n');
+}
+
+function get_int(iface, property) {
+ return int(get_str(iface, property));
+}
+
+function get_uptime() {
+ return int(split(readfile('/proc/uptime'), '.', 2)[0]);
+}
+
+function get_x_time(uptime, iface, property) {
+ let t = get_int(iface, property);
+ if (t > 0) {
+ t = uptime - t;
+ }
+ return t;
+}
+
+function ucibool(val) {
+ switch (val) {
+ case 'yes':
+ case 'on':
+ case 'true':
+ case 'enabled':
+ return true;
+ default:
+ return !!int(val);
+ }
+}
+
+function get_mwan3track_status(iface, uci_track_ips, procd) {
+ if (length(uci_track_ips) == 0) {
+ return 'disabled';
+ }
+ if (procd?.[sprintf('track_%s', iface)]?.running) {
+ const started = get_str(iface, 'STARTED');
+ switch (started) {
+ case '0':
+ return 'paused';
+ case '1':
+ return 'active';
+ default:
+ return 'down';
+ }
+ }
+ return 'down';
+}
+
+const connected_check_cmd = {
+ '4': 'iptables -t mangle -w -S mwan3_connected_ipv4',
+ '6': 'ip6tables -t mangle -w -S mwan3_connected_ipv6',
+};
+const ipset_save_re = regexp('^add mwan3_connected_ipv[46] (.*)\n$');
+
+function get_connected_ips(version) {
+ const check = popen(connected_check_cmd[version], 'r');
+ check.read('all');
+ if (check.close() != 0) {
+ return [];
+ }
+ const ipset = popen(sprintf('ipset -o save list mwan3_connected_ipv%s', version), 'r');
+ const ips = [];
+ for (let line = ipset.read('line'); length(line); line = ipset.read('line')) {
+ const m = match(line, ipset_save_re);
+ if (length(m) == 2) {
+ push(ips, m[1]);
+ }
+ }
+ ipset.close();
+ return ips;
+}
+
+const policies_cmd = {
+ '4': 'iptables -t mangle -w -S',
+ '6': 'ip6tables -t mangle -w -S'
+};
+const policies_re = regexp('^-A mwan3_policy_([^ ]+) .*?--comment "([^"]+)"');
+
+function get_policies(version) {
+ const ipt = popen(policies_cmd[version], 'r');
+ const policies = {};
+ for (let line = ipt.read('line'); length(line); line = ipt.read('line')) {
+ const m = match(line, policies_re);
+ if (m == null) {
+ continue;
+ }
+ const policy = m[1];
+ if (!exists(policies, policy)) {
+ policies[policy] = [];
+ }
+ const intfw = split(m[2], ' ', 3);
+ const weight = int(intfw[1]);
+ const total = int(intfw[2]);
+ if (weight >= 0 && total > 0) {
+ push(policies[policy], {
+ 'interface': intfw[0],
+ 'percent': weight / total * 100,
+ })
+ }
+ }
+ ipt.close();
+ return policies;
+}
+
+function interfaces_status(request) {
+ const uci = cursor();
+ const procd = ubus.call('service', 'list', { 'name': 'mwan3' })?.mwan3?.instances;
+ const interfaces = {};
+ uci.foreach('mwan3', 'interface', intf => {
+ const ifname = intf['.name'];
+ if (request.args.interface != null && request.args.interface != ifname) {
+ return;
+ }
+ const netstatus = ubus.call(sprintf('network.interface.%s', ifname), 'status', {});
+ const uptime = get_uptime();
+ const uci_track_ips = intf['track_ip'];
+ const track_status = get_mwan3track_status(ifname, uci_track_ips, procd);
+ const track_ips = [];
+ for (let ip in uci_track_ips) {
+ push(track_ips, {
+ 'ip': ip,
+ 'status': get_str(ifname, sprintf('TRACK_%s', ip)) || 'unknown',
+ 'latency': get_int(ifname, sprintf('LATENCY_%s', ip)),
+ 'packetloss': get_int(ifname, sprintf('LOSS_%s', ip)),
+ });
+ }
+ interfaces[ifname] = {
+ 'age': get_x_time(uptime, ifname, 'TIME'),
+ 'online': get_x_time(uptime, ifname, 'ONLINE'),
+ 'offline': get_x_time(uptime, ifname, 'OFFLINE'),
+ 'uptime': netstatus.uptime || 0,
+ 'score': get_int(ifname, 'SCORE'),
+ 'lost': get_int(ifname, 'LOST'),
+ 'turn': get_int(ifname, 'TURN'),
+ 'status': get_str(ifname, 'STATUS') || 'unknown',
+ 'enabled': ucibool(intf['enabled']),
+ 'running': track_status == 'active',
+ 'tracking': track_status,
+ 'up': netstatus.up,
+ 'track_ip': track_ips,
+ };
+ });
+ return interfaces;
+}
+
+const methods = {
+ status: {
+ args: {
+ section: 'section',
+ interface: 'interface'
+ },
+ call: function (request) {
+ switch (request.args.section) {
+ case 'connected':
+ return {
+ 'connected': {
+ 'ipv4': get_connected_ips('4'),
+ 'ipv6': get_connected_ips('6'),
+ },
+ };
+ case 'policies':
+ return {
+ 'policies': {
+ 'ipv4': get_policies('4'),
+ 'ipv6': get_policies('6'),
+ },
+ };
+ case 'interfaces':
+ return {
+ 'interfaces': interfaces_status(request),
+ };
+ default:
+ return {
+ 'interfaces': interfaces_status(request),
+ 'connected': {
+ 'ipv4': get_connected_ips('4'),
+ 'ipv6': get_connected_ips('6'),
+ },
+ 'policies': {
+ 'ipv4': get_policies('4'),
+ 'ipv6': get_policies('6'),
+ },
+ };
+ }
+ }
+ }
+};
+
+return { 'mwan3': methods };