Last active
May 15, 2023 12:34
-
-
Save baphael/046f20894d7e15009ab6184e6eda49ca to your computer and use it in GitHub Desktop.
Netstat overlay to monitor TCP sockets usage (polling)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# Simple IPv4 PCRE pattern | |
IP_P="(\d{1,3}\.){3}\d{1,3}" | |
echo -n "Check privileges... " | |
if (( $(id -u) )); then | |
echo KO | |
echo "This script must be executed as superuser in order to write in /tmp and to listen to all sockets." | |
exit 1 | |
fi | |
echo OK | |
echo -n "Check dependencies... " | |
netstat="$(which netstat)" | |
if [[ -z ${netstat} ]]; then | |
echo KO | |
echo "This script relies on netstat and it seems it is not installed. To install it, run 'apt install net-tools' as superuser." | |
exit 2 | |
fi | |
echo OK | |
function usage() { | |
SELF="$(basename ${0})" | |
cat <<EOF | |
Description: Retrieves distinct IPv4 addresses that connect to localhost (on some ports or all). | |
Usage: "${SELF}" [-p|--port PORT] [-l|--local] [-i|--interval SECONDS] [-o|--output FILE] [-h|--help] [-v|--verbose] | |
Press ^C [CTRL+c] to stop | |
Optional arguments: | |
-h, --help Display this help message and exit. | |
-p, --port PORT Port to check. Can be used multiple times to check multiple ports. Default is all ports. | |
-l, --include-lhost Include localhost connexions. | |
-i, --interval SECONDS Frequency at which checks are made. Cannot be below 1 nor above 59. Should be below 10 for optimal results. Default is 2. | |
-o, --output FILE Output file. Defaults to STDOUT. | |
-v, --verbose Verbose output (only to STDOUT). | |
Examples: | |
"${SELF}" -p 80 -p 443 -i 5 # Check who connects to my web services | |
"${SELF}" -p 3306 -p 5432 -l -o my_db_clients.log # Check who connects to my MySQL/PostgreSQL | |
"${SELF}" -v -p 0-1024 # Check who connects to all my below-1024 ports | |
Notes: | |
Compact arguments notation is not supported (e.g. : "${SELF} -vlp 80" will not work). | |
EOF | |
} | |
echo -n "Parse arguments... " | |
# Arguments parsing | |
MY_OWN_IPS=($(ip a | grep -oP "${IP_P}(?=/\d+)")) | |
PORTS=() | |
INTERVAL=2 | |
OUTPUT="" | |
VERBOSE=0 | |
DEBUG=0 | |
while (( $# )); do | |
case $1 in | |
-l|--include-lhost) | |
MY_OWN_IPS=() | |
shift | |
;; | |
-p|--port) | |
if (( $(echo "${2}" | grep -Pc "^\d+?$") )); then | |
PORTS+=( "${2}" ) | |
elif (( $(echo "${2}" | grep -Pc "^\d+-\d+?$") )); then | |
port_range_lower_bound=$(echo "${2}"|cut -d- -f1) | |
port_range_upper_bound=$(echo "${2}"|cut -d- -f2) | |
if (( ! (${port_range_lower_bound} < ${port_range_upper_bound}) )); then | |
echo "Illegal port range. Assertion violated: ${port_range_lower_bound} < ${port_range_upper_bound} ! This port range will be ignored." | |
fi | |
PORTS+=($(seq ${port_range_lower_bound} ${port_range_upper_bound})) | |
else | |
echo KO | |
echo "Illegal port value ${2} !" | |
exit 3 | |
fi | |
shift | |
shift | |
;; | |
-i|--interval) | |
if (( $(echo "${2}" | grep -Pc "^\d+$") )); then | |
if (( "${2}" >= 10 )); then | |
echo "WARNING: for optimal results, delay value should be < 10 (seconds) !" | |
fi | |
if (( "${2}" < 1 || "${2}" > 59 )); then | |
echo KO | |
echo "Assertion violated: 1 < ${2} < 59 !" | |
exit 4 | |
fi | |
INTERVAL="${2}" | |
fi | |
shift | |
shift | |
;; | |
-o|--output) | |
touch "${2}" | |
if (( ! $? )) && [[ -f "${2}" ]]; then | |
OUTPUT="${2}" | |
else | |
echo KO | |
echo "Could not write to output file." | |
exit 5 | |
fi | |
shift | |
shift | |
;; | |
-h|--help) | |
usage | |
exit 6 | |
;; | |
-v|--verbose) | |
VERBOSE=1 | |
shift | |
;; | |
-d|--debug) | |
DEBUG=1 | |
shift | |
;; | |
*) | |
echo KO | |
echo "Unknown argument ${1} !" | |
usage | |
exit 7 | |
;; | |
esac | |
done | |
echo OK | |
if (( ${VERBOSE} )); then | |
echo "OUTPUT: ${OUTPUT:-STDOUT}" | |
echo "MONITORED PORTS: ${PORTS[@]:-*}" | |
echo "VERBOSE MODE: $((( ${VERBOSE} )) && echo En || echo Dis)abled" | |
echo "INTERVAL: ${INTERVAL:-2} seconds" | |
echo "SHOW LOCAL CONNEXIONS: $((( ${#MY_OWN_IPS[@]} )) && echo No || echo Yes)" | |
fi | |
# Build ports regexp | |
PORTS_REGEXP="" | |
if (( ${#PORTS[@]} )); then | |
PORTS_REGEXP=":(" | |
for port in ${PORTS[@]}; do | |
PORTS_REGEXP+="${port}|" | |
done | |
PORTS_REGEXP="${PORTS_REGEXP%|})\b" | |
fi | |
(( ${VERBOSE} )) && echo "MONITORED PORTS REGEXP: ${PORTS_REGEXP}" | |
TMP_FILE="/tmp/whobinds_$(date +%s%N).log" | |
touch "${TMP_FILE}" | |
(( ${VERBOSE} )) && echo "TMP FILE: ${TMP_FILE}" | |
function cleanup() { | |
echo -n "Graceful stop... " | |
rm -rf "${TMP_FILE}" 2>/dev/null | |
echo OK | |
exit 10 | |
} | |
trap cleanup SIGHUP SIGINT SIGQUIT SIGABRT | |
echo "Running..." | |
echo "Press [ctrl+c] to interrupt." | |
while true; do | |
unset is_local lhost lport rhost output_str | |
# Every INTERVAL seconds, we check which IPs connect to specified port(s) | |
for lhost_lport_rhost_rport in $(netstat -ntp | grep -P "${PORTS_REGEXP}" | grep -oP "(${IP_P}:\d+\s*){2}" | sed "s/\s*$//" | tr -s " " | tr " " "|" | sort | uniq); do | |
(( ${DEBUG} )) && echo lhost_lport_rhost_rport:${lhost_lport_rhost_rport} | |
lhost=$(echo "${lhost_lport_rhost_rport}" | grep -oP "^${IP_P}(?=:\d+\|${IP_P}:\d+$)"); (( ${DEBUG} )) && echo lhost:$lhost | |
lport=$(echo "${lhost_lport_rhost_rport}" | grep -oP "^${IP_P}:\K\d+(?=\|${IP_P}:\d+$)"); (( ${DEBUG} )) && echo lport:$lport | |
rhost=$(echo "${lhost_lport_rhost_rport}" | grep -oP "^${IP_P}:\d+\|\K${IP_P}(?=:\d+$)"); (( ${DEBUG} )) && echo rhost $rhost | |
rport=$(echo "${lhost_lport_rhost_rport}" | grep -oP "^${IP_P}:\d+\|${IP_P}:\K\d+$"); (( ${DEBUG} )) && echo rport $rport | |
output_str="${lhost}:${lport} <--> ${rhost}:${rport}" | |
# For each of those IPs, we check if it was already seen | |
if (( ! $(grep -cow "^${lhost_lport_rhost_rport}$" "${TMP_FILE}") )); then | |
# If not, we check if it's one of our own IP | |
is_local=$(echo ${MY_OWN_IPS[@]} | grep -cow "${rhost}") | |
if (( ! ${is_local} )); then | |
# And if it's not or if local addresses are to be incuded, then we have a match ! | |
echo "${lhost_lport_rhost_rport}" >> "${TMP_FILE}" | |
echo "${output_str}" | tee -a "${OUTPUT}" 2>/dev/null | |
fi | |
fi | |
done | |
sleep ${INTERVAL} | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment