diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-03-09 13:19:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-03-09 13:19:22 +0000 |
commit | c21c3b0befeb46a51b6bf3758ffa30813bea0ff0 (patch) | |
tree | 9754ff1ca740f6346cf8483ec915d4054bc5da2d /collectors/cgroups.plugin/cgroup-network-helper.sh | |
parent | Adding upstream version 1.43.2. (diff) | |
download | netdata-c21c3b0befeb46a51b6bf3758ffa30813bea0ff0.tar.xz netdata-c21c3b0befeb46a51b6bf3758ffa30813bea0ff0.zip |
Adding upstream version 1.44.3.upstream/1.44.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rwxr-xr-x | collectors/cgroups.plugin/cgroup-network-helper.sh | 320 | ||||
-rwxr-xr-x | collectors/cgroups.plugin/cgroup-network-helper.sh.in | 376 |
2 files changed, 376 insertions, 320 deletions
diff --git a/collectors/cgroups.plugin/cgroup-network-helper.sh b/collectors/cgroups.plugin/cgroup-network-helper.sh deleted file mode 100755 index 008bc987f..000000000 --- a/collectors/cgroups.plugin/cgroup-network-helper.sh +++ /dev/null @@ -1,320 +0,0 @@ -#!/usr/bin/env bash -# shellcheck disable=SC1117 - -# cgroup-network-helper.sh -# detect container and virtual machine interfaces -# -# (C) 2017 Costa Tsaousis -# SPDX-License-Identifier: GPL-3.0-or-later -# -# This script is called as root (by cgroup-network), with either a pid, or a cgroup path. -# It tries to find all the network interfaces that belong to the same cgroup. -# -# It supports several method for this detection: -# -# 1. cgroup-network (the binary father of this script) detects veth network interfaces, -# by examining iflink and ifindex IDs and switching namespaces -# (it also detects the interface name as it is used by the container). -# -# 2. this script, uses /proc/PID/fdinfo to find tun/tap network interfaces. -# -# 3. this script, calls virsh to find libvirt network interfaces. -# - -# ----------------------------------------------------------------------------- - -# the system path is cleared by cgroup-network -# shellcheck source=/dev/null -[ -f /etc/profile ] && source /etc/profile - -export LC_ALL=C - -PROGRAM_NAME="$(basename "${0}")" - -LOG_LEVEL_ERR=1 -LOG_LEVEL_WARN=2 -LOG_LEVEL_INFO=3 -LOG_LEVEL="$LOG_LEVEL_INFO" - -set_log_severity_level() { - case ${NETDATA_LOG_SEVERITY_LEVEL,,} in - "info") LOG_LEVEL="$LOG_LEVEL_INFO";; - "warn" | "warning") LOG_LEVEL="$LOG_LEVEL_WARN";; - "err" | "error") LOG_LEVEL="$LOG_LEVEL_ERR";; - esac -} - -set_log_severity_level - -logdate() { - date "+%Y-%m-%d %H:%M:%S" -} - -log() { - local status="${1}" - shift - - echo >&2 "$(logdate): ${PROGRAM_NAME}: ${status}: ${*}" - -} - -info() { - [[ -n "$LOG_LEVEL" && "$LOG_LEVEL_INFO" -gt "$LOG_LEVEL" ]] && return - log INFO "${@}" -} - -warning() { - [[ -n "$LOG_LEVEL" && "$LOG_LEVEL_WARN" -gt "$LOG_LEVEL" ]] && return - log WARNING "${@}" -} - -error() { - [[ -n "$LOG_LEVEL" && "$LOG_LEVEL_ERR" -gt "$LOG_LEVEL" ]] && return - log ERROR "${@}" -} - -fatal() { - log FATAL "${@}" - exit 1 -} - -debug=${NETDATA_CGROUP_NETWORK_HELPER_DEBUG=0} -debug() { - [ "${debug}" = "1" ] && log DEBUG "${@}" -} - -# ----------------------------------------------------------------------------- -# check for BASH v4+ (required for associative arrays) - -[ $(( BASH_VERSINFO[0] )) -lt 4 ] && \ - fatal "BASH version 4 or later is required (this is ${BASH_VERSION})." - -# ----------------------------------------------------------------------------- -# parse the arguments - -pid= -cgroup= -while [ -n "${1}" ] -do - case "${1}" in - --cgroup) cgroup="${2}"; shift 1;; - --pid|-p) pid="${2}"; shift 1;; - --debug|debug) debug=1;; - *) fatal "Cannot understand argument '${1}'";; - esac - - shift -done - -if [ -z "${pid}" ] && [ -z "${cgroup}" ] -then - fatal "Either --pid or --cgroup is required" -fi - -# ----------------------------------------------------------------------------- - -set_source() { - [ ${debug} -eq 1 ] && echo "SRC ${*}" -} - - -# ----------------------------------------------------------------------------- -# veth interfaces via cgroup - -# cgroup-network can detect veth interfaces by itself (written in C). -# If you seek for a shell version of what it does, check this: -# https://github.com/netdata/netdata/issues/474#issuecomment-317866709 - - -# ----------------------------------------------------------------------------- -# tun/tap interfaces via /proc/PID/fdinfo - -# find any tun/tap devices linked to a pid -proc_pid_fdinfo_iff() { - local p="${1}" # the pid - - debug "Searching for tun/tap interfaces for pid ${p}..." - set_source "fdinfo" - grep "^iff:.*" "${NETDATA_HOST_PREFIX}/proc/${p}/fdinfo"/* 2>/dev/null | cut -f 2 -} - -find_tun_tap_interfaces_for_cgroup() { - local c="${1}" # the cgroup path - [ -d "${c}/emulator" ] && c="${c}/emulator" # check for 'emulator' subdirectory - c="${c}/cgroup.procs" # make full path - - # for each pid of the cgroup - # find any tun/tap devices linked to the pid - if [ -f "${c}" ] - then - local p - for p in $(< "${c}" ) - do - proc_pid_fdinfo_iff "${p}" - done - else - debug "Cannot find file '${c}', not searching for tun/tap interfaces." - fi -} - - -# ----------------------------------------------------------------------------- -# virsh domain network interfaces - -virsh_cgroup_to_domain_name() { - local c="${1}" # the cgroup path - - debug "extracting a possible virsh domain from cgroup ${c}..." - - # extract for the cgroup path - sed -n -e "s|.*/machine-qemu\\\\x2d[0-9]\+\\\\x2d\(.*\)\.scope$|\1|p" \ - -e "s|.*/machine/qemu-[0-9]\+-\(.*\)\.libvirt-qemu$|\1|p" \ - -e "s|.*/machine/\(.*\)\.libvirt-qemu$|\1|p" \ - <<EOF -${c} -EOF -} - -virsh_find_all_interfaces_for_cgroup() { - local c="${1}" # the cgroup path - - # the virsh command - local virsh - # shellcheck disable=SC2230 - virsh="$(which virsh 2>/dev/null || command -v virsh 2>/dev/null)" - - if [ -n "${virsh}" ] - then - local d - d="$(virsh_cgroup_to_domain_name "${c}")" - # convert hex to character - # e.g.: vm01\x2dweb => vm01-web (https://github.com/netdata/netdata/issues/11088#issuecomment-832618149) - d="$(printf '%b' "${d}")" - - if [ -n "${d}" ] - then - debug "running: virsh domiflist ${d}; to find the network interfaces" - - # 'virsh -r domiflist <domain>' example output - # Interface Type Source Model MAC - #-------------------------------------------------------------- - # vnet3 bridge br0 virtio 52:54:00:xx:xx:xx - # vnet4 network default virtio 52:54:00:yy:yy:yy - - # match only 'network' interfaces from virsh output - set_source "virsh" - "${virsh}" -r domiflist "${d}" |\ - sed -n \ - -e "s|^[[:space:]]\?\([^[:space:]]\+\)[[:space:]]\+network[[:space:]]\+\([^[:space:]]\+\)[[:space:]]\+[^[:space:]]\+[[:space:]]\+[^[:space:]]\+$|\1 \1_\2|p" \ - -e "s|^[[:space:]]\?\([^[:space:]]\+\)[[:space:]]\+bridge[[:space:]]\+\([^[:space:]]\+\)[[:space:]]\+[^[:space:]]\+[[:space:]]\+[^[:space:]]\+$|\1 \1_\2|p" - else - debug "no virsh domain extracted from cgroup ${c}" - fi - else - debug "virsh command is not available" - fi -} - -# ----------------------------------------------------------------------------- -# netnsid detected interfaces - -netnsid_find_all_interfaces_for_pid() { - local pid="${1}" - [ -z "${pid}" ] && return 1 - - local nsid - nsid=$(lsns -t net -p "${pid}" -o NETNSID -nr 2>/dev/null) - if [ -z "${nsid}" ] || [ "${nsid}" = "unassigned" ]; then - return 1 - fi - - set_source "netnsid" - ip link show |\ - grep -B 1 -E " link-netnsid ${nsid}($| )" |\ - sed -n -e "s|^[[:space:]]*[0-9]\+:[[:space:]]\+\([A-Za-z0-9_]\+\)\(@[A-Za-z0-9_]\+\)*:[[:space:]].*$|\1|p" -} - -netnsid_find_all_interfaces_for_cgroup() { - local c="${1}" # the cgroup path - - if [ -f "${c}/cgroup.procs" ]; then - netnsid_find_all_interfaces_for_pid "$(head -n 1 "${c}/cgroup.procs" 2>/dev/null)" - else - debug "Cannot find file '${c}/cgroup.procs', not searching for netnsid interfaces." - fi -} - -# ----------------------------------------------------------------------------- - -find_all_interfaces_of_pid_or_cgroup() { - local p="${1}" c="${2}" # the pid and the cgroup path - - if [ -n "${pid}" ] - then - # we have been called with a pid - - proc_pid_fdinfo_iff "${p}" - netnsid_find_all_interfaces_for_pid "${p}" - - elif [ -n "${c}" ] - then - # we have been called with a cgroup - - info "searching for network interfaces of cgroup '${c}'" - - find_tun_tap_interfaces_for_cgroup "${c}" - virsh_find_all_interfaces_for_cgroup "${c}" - netnsid_find_all_interfaces_for_cgroup "${c}" - - else - - error "Either a pid or a cgroup path is needed" - return 1 - - fi - - return 0 -} - -# ----------------------------------------------------------------------------- - -# an associative array to store the interfaces -# the index is the interface name as seen by the host -# the value is the interface name as seen by the guest / container -declare -A devs=() - -# store all interfaces found in the associative array -# this will also give the unique devices, as seen by the host -last_src= -# shellcheck disable=SC2162 -while read host_device guest_device -do - [ -z "${host_device}" ] && continue - - [ "${host_device}" = "SRC" ] && last_src="${guest_device}" && continue - - # the default guest_device is the host_device - [ -z "${guest_device}" ] && guest_device="${host_device}" - - # when we run in debug, show the source - debug "Found host device '${host_device}', guest device '${guest_device}', detected via '${last_src}'" - - if [ -z "${devs[${host_device}]}" ] || [ "${devs[${host_device}]}" = "${host_device}" ]; then - devs[${host_device}]="${guest_device}" - fi - -done < <( find_all_interfaces_of_pid_or_cgroup "${pid}" "${cgroup}" ) - -# print the interfaces found, in the format netdata expects them -found=0 -for x in "${!devs[@]}" -do - found=$((found + 1)) - echo "${x} ${devs[${x}]}" -done - -debug "found ${found} network interfaces for pid '${pid}', cgroup '${cgroup}', run as ${USER}, ${UID}" - -# let netdata know if we found any -[ ${found} -eq 0 ] && exit 1 -exit 0 diff --git a/collectors/cgroups.plugin/cgroup-network-helper.sh.in b/collectors/cgroups.plugin/cgroup-network-helper.sh.in new file mode 100755 index 000000000..da9b9162a --- /dev/null +++ b/collectors/cgroups.plugin/cgroup-network-helper.sh.in @@ -0,0 +1,376 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1117 + +# cgroup-network-helper.sh +# detect container and virtual machine interfaces +# +# (C) 2023 Netdata Inc. +# SPDX-License-Identifier: GPL-3.0-or-later +# +# This script is called as root (by cgroup-network), with either a pid, or a cgroup path. +# It tries to find all the network interfaces that belong to the same cgroup. +# +# It supports several method for this detection: +# +# 1. cgroup-network (the binary father of this script) detects veth network interfaces, +# by examining iflink and ifindex IDs and switching namespaces +# (it also detects the interface name as it is used by the container). +# +# 2. this script, uses /proc/PID/fdinfo to find tun/tap network interfaces. +# +# 3. this script, calls virsh to find libvirt network interfaces. +# + +# ----------------------------------------------------------------------------- + +# the system path is cleared by cgroup-network +# shellcheck source=/dev/null +[ -f /etc/profile ] && source /etc/profile +export PATH="${PATH}:@sbindir_POST@" + +export LC_ALL=C + +cmd_line="'${0}' $(printf "'%s' " "${@}")" + +# ----------------------------------------------------------------------------- +# logging + +PROGRAM_NAME="$(basename "${0}")" + +# these should be the same with syslog() priorities +NDLP_EMERG=0 # system is unusable +NDLP_ALERT=1 # action must be taken immediately +NDLP_CRIT=2 # critical conditions +NDLP_ERR=3 # error conditions +NDLP_WARN=4 # warning conditions +NDLP_NOTICE=5 # normal but significant condition +NDLP_INFO=6 # informational +NDLP_DEBUG=7 # debug-level messages + +# the max (numerically) log level we will log +LOG_LEVEL=$NDLP_INFO + +set_log_min_priority() { + case "${NETDATA_LOG_LEVEL,,}" in + "emerg" | "emergency") + LOG_LEVEL=$NDLP_EMERG + ;; + + "alert") + LOG_LEVEL=$NDLP_ALERT + ;; + + "crit" | "critical") + LOG_LEVEL=$NDLP_CRIT + ;; + + "err" | "error") + LOG_LEVEL=$NDLP_ERR + ;; + + "warn" | "warning") + LOG_LEVEL=$NDLP_WARN + ;; + + "notice") + LOG_LEVEL=$NDLP_NOTICE + ;; + + "info") + LOG_LEVEL=$NDLP_INFO + ;; + + "debug") + LOG_LEVEL=$NDLP_DEBUG + ;; + esac +} + +set_log_min_priority + +log() { + local level="${1}" + shift 1 + + [[ -n "$level" && -n "$LOG_LEVEL" && "$level" -gt "$LOG_LEVEL" ]] && return + + systemd-cat-native --log-as-netdata --newline="--NEWLINE--" <<EOFLOG +INVOCATION_ID=${NETDATA_INVOCATION_ID} +SYSLOG_IDENTIFIER=${PROGRAM_NAME} +PRIORITY=${level} +THREAD_TAG=cgroup-network-helper +ND_LOG_SOURCE=collector +ND_REQUEST=${cmd_line} +MESSAGE=${*//\\n/--NEWLINE--} + +EOFLOG + # AN EMPTY LINE IS NEEDED ABOVE +} + +info() { + log "$NDLP_INFO" "${@}" +} + +warning() { + log "$NDLP_WARN" "${@}" +} + +error() { + log "$NDLP_ERR" "${@}" +} + +fatal() { + log "$NDLP_ALERT" "${@}" + exit 1 +} + +debug() { + log "$NDLP_DEBUG" "${@}" +} + +debug=0 +if [ "${NETDATA_CGROUP_NETWORK_HELPER_DEBUG-0}" = "1" ]; then + debug=1 + LOG_LEVEL=$NDLP_DEBUG +fi + +# ----------------------------------------------------------------------------- +# check for BASH v4+ (required for associative arrays) + +if [ ${BASH_VERSINFO[0]} -lt 4 ]; then + echo >&2 "BASH version 4 or later is required (this is ${BASH_VERSION})." + exit 1 +fi + +# ----------------------------------------------------------------------------- +# parse the arguments + +pid= +cgroup= +while [ -n "${1}" ] +do + case "${1}" in + --cgroup) cgroup="${2}"; shift 1;; + --pid|-p) pid="${2}"; shift 1;; + --debug|debug) + debug=1 + LOG_LEVEL=$NDLP_DEBUG + ;; + *) fatal "Cannot understand argument '${1}'";; + esac + + shift +done + +if [ -z "${pid}" ] && [ -z "${cgroup}" ] +then + fatal "Either --pid or --cgroup is required" +fi + +# ----------------------------------------------------------------------------- + +set_source() { + [ ${debug} -eq 1 ] && echo "SRC ${*}" +} + + +# ----------------------------------------------------------------------------- +# veth interfaces via cgroup + +# cgroup-network can detect veth interfaces by itself (written in C). +# If you seek for a shell version of what it does, check this: +# https://github.com/netdata/netdata/issues/474#issuecomment-317866709 + + +# ----------------------------------------------------------------------------- +# tun/tap interfaces via /proc/PID/fdinfo + +# find any tun/tap devices linked to a pid +proc_pid_fdinfo_iff() { + local p="${1}" # the pid + + debug "Searching for tun/tap interfaces for pid ${p}..." + set_source "fdinfo" + grep "^iff:.*" "${NETDATA_HOST_PREFIX}/proc/${p}/fdinfo"/* 2>/dev/null | cut -f 2 +} + +find_tun_tap_interfaces_for_cgroup() { + local c="${1}" # the cgroup path + [ -d "${c}/emulator" ] && c="${c}/emulator" # check for 'emulator' subdirectory + c="${c}/cgroup.procs" # make full path + + # for each pid of the cgroup + # find any tun/tap devices linked to the pid + if [ -f "${c}" ] + then + local p + for p in $(< "${c}" ) + do + proc_pid_fdinfo_iff "${p}" + done + else + debug "Cannot find file '${c}', not searching for tun/tap interfaces." + fi +} + + +# ----------------------------------------------------------------------------- +# virsh domain network interfaces + +virsh_cgroup_to_domain_name() { + local c="${1}" # the cgroup path + + debug "extracting a possible virsh domain from cgroup ${c}..." + + # extract for the cgroup path + sed -n -e "s|.*/machine-qemu\\\\x2d[0-9]\+\\\\x2d\(.*\)\.scope$|\1|p" \ + -e "s|.*/machine/qemu-[0-9]\+-\(.*\)\.libvirt-qemu$|\1|p" \ + -e "s|.*/machine/\(.*\)\.libvirt-qemu$|\1|p" \ + <<EOF +${c} +EOF +} + +virsh_find_all_interfaces_for_cgroup() { + local c="${1}" # the cgroup path + + # the virsh command + local virsh + # shellcheck disable=SC2230 + virsh="$(which virsh 2>/dev/null || command -v virsh 2>/dev/null)" + + if [ -n "${virsh}" ] + then + local d + d="$(virsh_cgroup_to_domain_name "${c}")" + # convert hex to character + # e.g.: vm01\x2dweb => vm01-web (https://github.com/netdata/netdata/issues/11088#issuecomment-832618149) + d="$(printf '%b' "${d}")" + + if [ -n "${d}" ] + then + debug "running: virsh domiflist ${d}; to find the network interfaces" + + # 'virsh -r domiflist <domain>' example output + # Interface Type Source Model MAC + #-------------------------------------------------------------- + # vnet3 bridge br0 virtio 52:54:00:xx:xx:xx + # vnet4 network default virtio 52:54:00:yy:yy:yy + + # match only 'network' interfaces from virsh output + set_source "virsh" + "${virsh}" -r domiflist "${d}" |\ + sed -n \ + -e "s|^[[:space:]]\?\([^[:space:]]\+\)[[:space:]]\+network[[:space:]]\+\([^[:space:]]\+\)[[:space:]]\+[^[:space:]]\+[[:space:]]\+[^[:space:]]\+$|\1 \1_\2|p" \ + -e "s|^[[:space:]]\?\([^[:space:]]\+\)[[:space:]]\+bridge[[:space:]]\+\([^[:space:]]\+\)[[:space:]]\+[^[:space:]]\+[[:space:]]\+[^[:space:]]\+$|\1 \1_\2|p" + else + debug "no virsh domain extracted from cgroup ${c}" + fi + else + debug "virsh command is not available" + fi +} + +# ----------------------------------------------------------------------------- +# netnsid detected interfaces + +netnsid_find_all_interfaces_for_pid() { + local pid="${1}" + [ -z "${pid}" ] && return 1 + + local nsid + nsid=$(lsns -t net -p "${pid}" -o NETNSID -nr 2>/dev/null) + if [ -z "${nsid}" ] || [ "${nsid}" = "unassigned" ]; then + return 1 + fi + + set_source "netnsid" + ip link show |\ + grep -B 1 -E " link-netnsid ${nsid}($| )" |\ + sed -n -e "s|^[[:space:]]*[0-9]\+:[[:space:]]\+\([A-Za-z0-9_]\+\)\(@[A-Za-z0-9_]\+\)*:[[:space:]].*$|\1|p" +} + +netnsid_find_all_interfaces_for_cgroup() { + local c="${1}" # the cgroup path + + if [ -f "${c}/cgroup.procs" ]; then + netnsid_find_all_interfaces_for_pid "$(head -n 1 "${c}/cgroup.procs" 2>/dev/null)" + else + debug "Cannot find file '${c}/cgroup.procs', not searching for netnsid interfaces." + fi +} + +# ----------------------------------------------------------------------------- + +find_all_interfaces_of_pid_or_cgroup() { + local p="${1}" c="${2}" # the pid and the cgroup path + + if [ -n "${pid}" ] + then + # we have been called with a pid + + proc_pid_fdinfo_iff "${p}" + netnsid_find_all_interfaces_for_pid "${p}" + + elif [ -n "${c}" ] + then + # we have been called with a cgroup + + info "searching for network interfaces of cgroup '${c}'" + + find_tun_tap_interfaces_for_cgroup "${c}" + virsh_find_all_interfaces_for_cgroup "${c}" + netnsid_find_all_interfaces_for_cgroup "${c}" + + else + + error "Either a pid or a cgroup path is needed" + return 1 + + fi + + return 0 +} + +# ----------------------------------------------------------------------------- + +# an associative array to store the interfaces +# the index is the interface name as seen by the host +# the value is the interface name as seen by the guest / container +declare -A devs=() + +# store all interfaces found in the associative array +# this will also give the unique devices, as seen by the host +last_src= +# shellcheck disable=SC2162 +while read host_device guest_device +do + [ -z "${host_device}" ] && continue + + [ "${host_device}" = "SRC" ] && last_src="${guest_device}" && continue + + # the default guest_device is the host_device + [ -z "${guest_device}" ] && guest_device="${host_device}" + + # when we run in debug, show the source + debug "Found host device '${host_device}', guest device '${guest_device}', detected via '${last_src}'" + + if [ -z "${devs[${host_device}]}" ] || [ "${devs[${host_device}]}" = "${host_device}" ]; then + devs[${host_device}]="${guest_device}" + fi + +done < <( find_all_interfaces_of_pid_or_cgroup "${pid}" "${cgroup}" ) + +# print the interfaces found, in the format netdata expects them +found=0 +for x in "${!devs[@]}" +do + found=$((found + 1)) + echo "${x} ${devs[${x}]}" +done + +debug "found ${found} network interfaces for pid '${pid}', cgroup '${cgroup}', run as ${USER}, ${UID}" + +# let netdata know if we found any +[ ${found} -eq 0 ] && exit 1 +exit 0 |