- _policy_read: remove erroneous _POLICY_DEFAULTS merge (introduced during split) - fmt.sh: fmt::bytes extracted from cmd::activity::_fmt_bytes - identity/subnet/policy list: ui::sort_rows applied - ctx::policies moved from policy.module.sh to context.sh
190 lines
5.2 KiB
Bash
190 lines
5.2 KiB
Bash
#!/usr/bin/env bash
|
|
# activity.command.sh — WireGuard activity snapshot
|
|
|
|
# ============================================
|
|
# Lifecycle
|
|
# ============================================
|
|
|
|
function cmd::activity::on_load() {
|
|
load_module net
|
|
|
|
flag::register --peer
|
|
flag::register --service
|
|
flag::register --ip
|
|
flag::register --hours
|
|
flag::register --type
|
|
flag::register --dropped
|
|
}
|
|
|
|
# ============================================
|
|
# Help
|
|
# ============================================
|
|
|
|
function cmd::activity::help() {
|
|
cat <<EOF
|
|
Usage: wgctl activity [options]
|
|
|
|
Show WireGuard activity — transfer totals and firewall drops per peer.
|
|
Data sources: wg show transfer, fw_events.log
|
|
|
|
Options:
|
|
--peer <name> Filter by peer name
|
|
--service <name> Filter by service (e.g. truenas, proxmox:web-ui)
|
|
--ip <ip> Filter by destination IP
|
|
--hours <n> Time window in hours (default: 24, 0 = all time)
|
|
--type <type> Filter by device type (combined with --peer)
|
|
--dropped Show only peers with at least one drop
|
|
|
|
Examples:
|
|
wgctl activity
|
|
wgctl activity --dropped
|
|
wgctl activity --peer phone-nuno
|
|
wgctl activity --service truenas
|
|
wgctl activity --hours 0
|
|
wgctl activity --ip 10.0.0.101
|
|
EOF
|
|
}
|
|
|
|
# ============================================
|
|
# Run
|
|
# ============================================
|
|
|
|
function cmd::activity::run() {
|
|
local filter_peer="" filter_service="" filter_ip="" filter_type=""
|
|
local hours=24 dropped_only=false
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--peer) filter_peer="$2"; shift 2 ;;
|
|
--service) filter_service="$2"; shift 2 ;;
|
|
--ip) filter_ip="$2"; shift 2 ;;
|
|
--type) filter_type="$2"; shift 2 ;;
|
|
--hours) hours="$2"; shift 2 ;;
|
|
--dropped) dropped_only=true; shift ;;
|
|
--help) cmd::activity::help; return ;;
|
|
*)
|
|
log::error "Unknown flag: $1"
|
|
cmd::activity::help
|
|
return 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Resolve peer name if type provided
|
|
if [[ -n "$filter_peer" && -n "$filter_type" ]]; then
|
|
filter_peer=$(peers::resolve_and_require "$filter_peer" "$filter_type") || return 1
|
|
fi
|
|
|
|
# Resolve --service to IP
|
|
local service_ip=""
|
|
if [[ -n "$filter_service" ]]; then
|
|
service_ip=$(net::resolve "$filter_service" 2>/dev/null | head -1 | cut -d: -f1) || true
|
|
if [[ -z "$service_ip" ]]; then
|
|
log::error "Service not found: ${filter_service}"
|
|
return 1
|
|
fi
|
|
fi
|
|
[[ -n "$filter_ip" ]] && service_ip="$filter_ip"
|
|
|
|
# Fetch aggregated data
|
|
local data
|
|
data=$(json::activity_aggregate \
|
|
"$(ctx::fw_events_log)" \
|
|
"$(ctx::events_log)" \
|
|
"$(config::interface)" \
|
|
"$(ctx::net)" \
|
|
"$(ctx::clients)" \
|
|
"$(ctx::meta)" \
|
|
"$hours" \
|
|
"$filter_peer" \
|
|
"$service_ip" 2>/dev/null)
|
|
|
|
if [[ -z "$data" ]]; then
|
|
log::wg_warning "No activity data found"
|
|
return 0
|
|
fi
|
|
|
|
# Measure column widths
|
|
local w_peer=16 w_drops=1
|
|
while IFS='|' read -r type rest; do
|
|
case "$type" in
|
|
peer)
|
|
local name drops
|
|
name=$(echo "$rest" | cut -d'|' -f1)
|
|
drops=$(echo "$rest" | cut -d'|' -f4)
|
|
(( ${#name} > w_peer )) && w_peer=${#name}
|
|
(( ${#drops} > w_drops )) && w_drops=${#drops}
|
|
;;
|
|
service)
|
|
local count
|
|
count=$(echo "$rest" | cut -d'|' -f3)
|
|
(( ${#count} > w_drops )) && w_drops=${#count}
|
|
;;
|
|
esac
|
|
done <<< "$data"
|
|
|
|
(( w_peer += 2 ))
|
|
|
|
# Compute column where drop count starts on peer row:
|
|
# " " (2) + name (w_peer) + " ↓" (3) + rx (10) + " ↑" (3) + tx (10) + " " (2)
|
|
# ↓ and ↑ are multi-byte (3 bytes, 1 visible) — 2 extra bytes each
|
|
# Visible: 2 + w_peer + 2+1 + 10 + 2+1 + 10 + 2 = w_peer + 30
|
|
local drops_col=$(( w_peer + 30 ))
|
|
|
|
local hours_display="${hours}h"
|
|
[[ "$hours" == "0" ]] && hours_display="all time"
|
|
|
|
log::section "Activity Monitor (last ${hours_display})"
|
|
echo ""
|
|
|
|
local first_peer=true skip_peer=false
|
|
|
|
while IFS='|' read -r record_type rest; do
|
|
case "$record_type" in
|
|
peer)
|
|
local name rx tx drops
|
|
IFS='|' read -r name rx tx drops <<< "$rest"
|
|
|
|
skip_peer=false
|
|
if $dropped_only && [[ "$drops" -eq 0 ]]; then
|
|
skip_peer=true
|
|
continue
|
|
fi
|
|
|
|
$first_peer || echo ""
|
|
first_peer=false
|
|
|
|
local rx_fmt tx_fmt
|
|
rx_fmt=$(fmt::bytes "$rx")
|
|
tx_fmt=$(fmt::bytes "$tx")
|
|
|
|
local name_pad rx_pad tx_pad
|
|
name_pad=$(printf "%-${w_peer}s" "$name")
|
|
rx_pad=$(printf "%-10s" "$rx_fmt")
|
|
tx_pad=$(printf "%-10s" "$tx_fmt")
|
|
|
|
local drop_word="drops"
|
|
[[ "$drops" -eq 1 ]] && drop_word="drop"
|
|
|
|
ui::activity::peer_row \
|
|
"$name_pad" "$rx_pad" "$tx_pad" "$drops" "$drop_word" "$w_drops"
|
|
;;
|
|
|
|
service)
|
|
$skip_peer && continue
|
|
|
|
local peer dest_display drop_count
|
|
IFS='|' read -r peer dest_display drop_count <<< "$rest"
|
|
|
|
local svc_drop_word="drops"
|
|
[[ "$drop_count" -eq 1 ]] && svc_drop_word="drop"
|
|
|
|
ui::activity::service_row \
|
|
"$dest_display" "$drop_count" "$svc_drop_word" "$drops_col" "$w_drops"
|
|
;;
|
|
esac
|
|
done <<< "$data"
|
|
|
|
echo ""
|
|
}
|
|
|