mirror of
https://github.com/netdata/netdata.git
synced 2025-04-07 23:05:41 +00:00

* updated copyright notices everywhere (I hope) * Update makeself.lsm * Update coverity-scan.sh * make all newlines be linux, not windows * remove copyright from all files (the take it from the repo), unless it is printed to users
375 lines
10 KiB
Bash
Executable file
375 lines
10 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
#
|
|
# cgroup-network-helper.sh
|
|
# detect container and virtual machine interfaces
|
|
#
|
|
#
|
|
# 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.
|
|
#
|
|
# shellcheck disable=SC1117
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# 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
|