Script:
#!/bin/bash
#
# multi-tcpdump.sh
#
# Usage:
# ./multi-tcpdump.sh --hosts "host1 host2 ..." --filter "<tcpdump filter>"
#
# This script SSHs (as root) into each host provided in the --hosts argument and
# runs tcpdump with the specified filter.
#
# Features:
# - Forces pseudo-terminal allocation with ssh's -tt option.
# - Uses tcpdump's -l for line-buffered output.
# - Each output line is prefixed with the originating host name.
# - Both stdout and stderr are captured.
# - When you press CTRL+C, the entire process group (all SSH/tcpdump sessions) is killed.
#
usage() {
echo "Usage: $0 --hosts \"host1 host2 ...\" --filter \"<tcpdump filter>\""
exit 1
}
# Set up a trap that kills the entire process group when CTRL+C is pressed.
# The 'kill -- -$$' sends the signal to all processes in the current process group.
trap 'echo; echo "CTRL+C pressed. Terminating all sessions..."; kill -- -$$; exit 1' SIGINT
# Ensure that at least 4 arguments (2 options with values) are provided.
if [ "$#" -lt 4 ]; then
usage
fi
# Parse command-line arguments.
while [[ "$#" -gt 0 ]]; do
case "$1" in
--hosts)
if [ -n "$2" ]; then
IFS=' ' read -r -a HOSTS <<< "$2"
shift 2
else
echo "Error: --hosts requires a non-empty value."
usage
fi
;;
--filter)
if [ -n "$2" ]; then
FILTER="$2"
shift 2
else
echo "Error: --filter requires a non-empty value."
usage
fi
;;
*)
echo "Unknown parameter: $1"
usage
;;
esac
done
# Function to run tcpdump on a given host.
run_tcpdump() {
local host="$1"
ssh -tt root@"$host" "tcpdump -n -i any -l '$FILTER'" 2>&1 | \
awk -v h="[$host]" '{ print h, $0; fflush(); }'
}
# Start a tcpdump session on each host in the background.
for host in "${HOSTS[@]}"; do
echo "Starting tcpdump on $host with filter:"
echo " $FILTER"
run_tcpdump "$host" &
done
# Wait for all background processes.
wait
Example command:
./multi-tcpdump.sh \
--hosts "host1 host2" \
--filter "host 1.1.1.1"