#!/bin/sh -u # -*- sh -*- : << =cut =head1 NAME upnpc_ - Plugin to monitor routers via UPnP This plugin uses the upnpc utility (package miniupnpc in Debian), to monitor an router using UPnP. It can monitor the following aspects, and plot them as separate graphs, or a single multigraph (if linked at upnpc or upnpc_multi: * uptime: how long the link has been up; * bitrate: the up and downlink bitrate (e.g., sync speed for DSL); * traffic: the actual up and downstream traffic rate; * pkts: the number of packets coming in and out. =head1 APPLICABLE SYSTEMS Linux systems with upnpc installed (miniupnpc package). =head1 CONFIGURATION If you do not want to show the link maximum bitrates, add the following plugin-configuration: [upnpc*] env.traffic_remove_max true You can display the graph on another host (e.g., the actual router) than the one running upnpc. To do so, first configure the plugin to use a different hostname. env.host_name router Then configure munin (in /etc/munin/munin-conf or /etc/munin/munin-conf.d), to support a new host. [example.net;router] address 127.0.0.1 use_node_name no =head1 AUTHOR Olivier Mehani Copyright (C) 2016--2021 Olivier Mehani =head1 LICENSE SPDX-License-Identifier: GPL-3.0-or-later =head1 MAGIC MARKERS #%# family=auto #%# capabilities=autoconf suggest =cut if [ "${MUNIN_DEBUG:-0}" = 1 ]; then set -x fi PLUGIN_NAME="$(basename "${0}")" MODE="$(echo "${PLUGIN_NAME}" | sed 's/.*_//')" # If called without a mode, default to multigraph [ "$MODE" = "upnpc" ] && MODE="multi" get_data() { if ! command -v upnpc >/dev/null; then echo "upnpc not found (miniupnpc package)" >&2 exit 1 fi upnpc -s } get_supported_modes() { DATA=$1 echo "${DATA}" | sed -n " \ s/.*Bytes.*/traffic/p; \ s/.*Packets.*/pkts/p; \ s/.*uptime=.*/uptime/p; \ " } autoconf() { if ! command -v upnpc >/dev/null; then echo "no (upnpc not found [miniupnpc package])" return fi upnpc -s 2>/dev/null | grep -q 'List.*devices.*found' && echo yes \ || echo "no (No UPnP router detected)" } suggest () { for mode in ${SUPPORTED_MODES}; do echo "${mode}" done echo "multi" } config () { case ${1} in "uptime") cat << EOF graph_title Uplink connection uptime${HOST_TITLE} graph_args -l 0 graph_category network graph_scale no graph_vlabel uptime in hours uptime.label uptime uptime.draw AREA uptime.cdef uptime,3600,/ ${HOST_NAME} EOF ;; "bitrate") cat << EOF graph_title [DEPRECATED] Uplink bitrate${HOST_TITLE} graph_args --base 1000 -l 0 graph_category network graph_vlabel bitrate down (-) / up (+) down.label bps up.label bps down.graph no up.negative down ${HOST_NAME} EOF ;; "root") cat << EOF graph_title Uplink traffic${HOST_TITLE} graph_args --base 1000 -l 0 graph_category network graph_vlabel bits per second in (-) / out (+) EOF graph_order="down=traffic.down up=traffic.up" if [ "${traffic_remove_max:-false}" != 'true' ]; then graph_order="${graph_order} maxdown=traffic.maxdown maxup=traffic.maxup" fi echo "graph_order ${graph_order}" if [ "${traffic_remove_max:-false}" != 'true' ]; then cat << EOF maxdown.label bps (max) maxup.label bps (max) maxdown.graph no maxup.negative maxdown EOF fi cat << EOF down.label bps down.cdef down,8,* down.graph no up.label bps up.cdef up,8,* up.negative down ${HOST_NAME} EOF ;; "traffic") cat << EOF graph_title Uplink traffic${HOST_TITLE} graph_args --base 1000 -l 0 graph_category network graph_vlabel bits per second in (-) / out (+) EOF if [ "${traffic_remove_max:-false}" != 'true' ]; then cat << EOF maxdown.label bps (max) maxup.label bps (max) maxdown.graph no maxup.negative maxdown EOF fi cat << EOF down.label bps down.type DERIVE down.min 0 down.cdef down,8,* up.label bps up.type DERIVE up.min 0 up.cdef up,8,* down.graph no up.negative down ${HOST_NAME} EOF ;; "pkts") # ${graph_period} is not a shell variable cat << EOF graph_title Uplink packets${HOST_TITLE} graph_args --base 1000 -l 0 graph_category network EOF # ${graph_period} is not a shell variable # shellcheck disable=SC2016 echo 'graph_vlabel packets in (-) / out (+) per ${graph_period}' cat << EOF down.label pps down.type DERIVE down.min 0 up.label pps up.type DERIVE up.min 0 down.graph no up.negative down ${HOST_NAME} EOF ;; "multi") echo "${HOST_NAME}" # Don't repeat HOST_NAME in sub-configs HOST_NAME="" for mode in ${SUPPORTED_MODES}; do echo "multigraph ${PLUGIN_NAME}.${mode}" config "${mode}" done echo "multigraph ${PLUGIN_NAME}" config "root" ;; *) echo "unknown mode '${1}'" >&2 exit 1 ;; esac } fetch () { case "${1}" in "uptime") echo "${DATA}" | sed -n "s/.*uptime=\([0-9]\+\)s.*/uptime.value \1/p" ;; "bitrate") echo "${DATA}" | sed -n "s/^MaxBitRateDown : \([0-9]\+\) bps.*MaxBitRateUp \([0-9]\+\) bps.*/down.value \1\nup.value \2/p" ;; "root") # Nothing to do: all values loaned from the traffic graph ;; "traffic") echo "${DATA}" | sed -n " s/^Bytes:\s*Sent:\s*\([0-9]\+\).*Recv:\s*\([0-9]\+\).*/up.value \1\ndown.value \2/p" if [ "${traffic_remove_max:-false}" != 'true' ]; then echo "${DATA}" | sed -n " s/^MaxBitRateDown : \([0-9]\+\) bps.*MaxBitRateUp \([0-9]\+\) bps.*/maxdown.value \1\nmaxup.value \2/p" fi ;; "pkts") echo "${DATA}" | sed -n "s/^Packets:\s*Sent:\s*\([0-9]\+\).*Recv:\s*\([0-9]\+\).*/up.value \1\ndown.value \2/p" ;; "multi"|"upnpc") for mode in ${SUPPORTED_MODES}; do echo "multigraph ${PLUGIN_NAME}.${mode}" fetch "${mode}" done echo "multigraph ${PLUGIN_NAME}" fetch "root" ;; *) echo "unknown mode '${1}'" >&2 exit 1 ;; esac } if [ "${1:-}" = "autoconf" ]; then autoconf exit 0 fi # do data-based detection here, rather than in # config() as we don't want to do this multiple times # when the function calls itself for multigraphs DATA=$(get_data) SUPPORTED_MODES=$(get_supported_modes "${DATA}") HOST=${host_name:-} HOST_TITLE="" HOST_NAME="host_name ${HOST}" if [ -z "${HOST}" ]; then HOST=$(echo "${DATA}" | sed -n "s#.*desc: http://\([^/:]\+\).*#\1#p") # Only add the host name to the title if autodetected HOST_TITLE=" ($HOST)" # ...but not as a separate host HOST_NAME="" fi case ${1:-} in "suggest") suggest ;; "config") config "${MODE}" if [ "${MUNIN_CAP_DIRTYCONFIG:-0}" = "1" ]; then fetch "${MODE}" fi ;; *) fetch "${MODE}" ;; esac