Skip to content

Instantly share code, notes, and snippets.

@vishalroygeek
Created July 29, 2025 06:23
Show Gist options
  • Select an option

  • Save vishalroygeek/da8394423f51888f296af4259c73d478 to your computer and use it in GitHub Desktop.

Select an option

Save vishalroygeek/da8394423f51888f296af4259c73d478 to your computer and use it in GitHub Desktop.
A bash script to count the number of packets received at TCP/UDP port(s) at defined interval ⌚
#!/bin/bash
# Get protocol, ports and interval from CLI
PROTOCOL=$1
PORTS=$2
INTERVAL=$3
if [ -z "$PROTOCOL" ] || [ -z "$PORTS" ] || [ -z "$INTERVAL" ]; then
echo "Usage: $0 <PROTOCOL> <PORTS> <INTERVAL>"
echo "PROTOCOL can be:"
echo " udp - Monitor UDP packets only"
echo " tcp - Monitor TCP packets only"
echo " both - Monitor both UDP and TCP packets"
echo "PORTS can be:"
echo " Single port: 8080"
echo " Multiple ports: 8080,8081,8082"
echo " Port range: 8080-8090"
echo "Example: $0 both '8080,8081,9000-9002' 10"
exit 1
fi
# Validate protocols
if [[ "$PROTOCOL" != "udp" && "$PROTOCOL" != "tcp" && "$PROTOCOL" != "both" ]]; then
echo "Error: Protocol must be 'udp', 'tcp', or 'both'"
exit 1
fi
echo "Monitoring $PROTOCOL packets on ports: $PORTS"
echo "Reporting every ${INTERVAL} seconds"
echo "Press Ctrl+C to stop"
echo ""
# Function to format tcpdump filters
build_filter() {
local protocol=$1
local ports=$2
local filter=""
local protocols=()
case $protocol in
"udp")
protocols=("udp")
;;
"tcp")
protocols=("tcp")
;;
"both")
protocols=("udp" "tcp")
;;
esac
IFS=',' read -ra PORT_PARTS <<< "$ports"
for part in "${PORT_PARTS[@]}"; do
if [[ $part == *"-"* ]]; then
# Handling port range (e.g., 8080-8090)
start_port=$(echo $part | cut -d'-' -f1)
end_port=$(echo $part | cut -d'-' -f2)
for ((p=start_port; p<=end_port; p++)); do
for proto in "${protocols[@]}"; do
if [ -z "$filter" ]; then
filter="$proto port $p"
else
filter="$filter or $proto port $p"
fi
done
done
else
# Handling single port
for proto in "${protocols[@]}"; do
if [ -z "$filter" ]; then
filter="$proto port $part"
else
filter="$filter or $proto port $part"
fi
done
fi
done
echo "$filter"
}
# Declaring associative arrays for port and protocol-specific counting
declare -A total_counts
declare -A interval_counts
# Initializing counters
IFS=',' read -ra PORT_PARTS <<< "$PORTS"
for part in "${PORT_PARTS[@]}"; do
if [[ $part == *"-"* ]]; then
start_port=$(echo $part | cut -d'-' -f1)
end_port=$(echo $part | cut -d'-' -f2)
for ((p=start_port; p<=end_port; p++)); do
if [[ "$PROTOCOL" == "both" ]]; then
total_counts["udp_$p"]=0
interval_counts["udp_$p"]=0
total_counts["tcp_$p"]=0
interval_counts["tcp_$p"]=0
elif [[ "$PROTOCOL" == "udp" ]]; then
total_counts["udp_$p"]=0
interval_counts["udp_$p"]=0
else
total_counts["tcp_$p"]=0
interval_counts["tcp_$p"]=0
fi
done
else
if [[ "$PROTOCOL" == "both" ]]; then
total_counts["udp_$part"]=0
interval_counts["udp_$part"]=0
total_counts["tcp_$part"]=0
interval_counts["tcp_$part"]=0
elif [[ "$PROTOCOL" == "udp" ]]; then
total_counts["udp_$part"]=0
interval_counts["udp_$part"]=0
else
total_counts["tcp_$part"]=0
interval_counts["tcp_$part"]=0
fi
fi
done
last_report=$(date +%s)
cleanup() {
echo "Monitoring stopped."
exit 0
}
trap cleanup INT
FILTER=$(build_filter "$PROTOCOL" "$PORTS")
# Monitor packets using tcpdump
sudo tcpdump -i any -n "$FILTER" 2>/dev/null | while read line; do
# Extracting port and protocol from tcpdump output
# UDP Format: "12:34:56.789 IP 1.2.3.4.12345 > 5.6.7.8.8080: UDP, length 100"
# TCP Format: "12:34:56.789 IP 1.2.3.4.12345 > 5.6.7.8.8080: Flags [S], seq 123, win 1024"
dest_port=$(echo "$line" | grep -o '\.[0-9]\+:' | tail -1 | sed 's/[.:]//g')
packet_protocol=""
if echo "$line" | grep -q "UDP"; then
packet_protocol="udp"
elif echo "$line" | grep -q "Flags\|tcp"; then
packet_protocol="tcp"
fi
# Counting the packet if we have both port and protocol
if [ -n "$dest_port" ] && [ -n "$packet_protocol" ]; then
key="${packet_protocol}_${dest_port}"
if [ -n "${total_counts[$key]}" ]; then
total_counts[$key]=$((${total_counts[$key]} + 1))
interval_counts[$key]=$((${interval_counts[$key]} + 1))
fi
fi
current_time=$(date +%s)
if [ $((current_time - last_report)) -ge $INTERVAL ]; then
echo "$(date '+%H:%M:%S'):"
interval_total=0
overall_total=0
declare -A ports_seen
for key in "${!total_counts[@]}"; do
port=$(echo $key | cut -d'_' -f2)
ports_seen[$port]=1
done
for port in $(printf '%s\n' "${!ports_seen[@]}" | sort -n); do
port_interval_total=0
port_overall_total=0
port_line=" Port $port:"
# Checking UDP stats for this port
if [ -n "${total_counts[udp_$port]}" ]; then
udp_interval=${interval_counts[udp_$port]}
udp_total=${total_counts[udp_$port]}
port_line="$port_line UDP: $udp_interval"
port_interval_total=$((port_interval_total + udp_interval))
port_overall_total=$((port_overall_total + udp_total))
interval_counts[udp_$port]=0
fi
# Checking TCP stats for this port
if [ -n "${total_counts[tcp_$port]}" ]; then
tcp_interval=${interval_counts[tcp_$port]}
tcp_total=${total_counts[tcp_$port]}
if [[ "$port_line" == *"UDP:"* ]]; then
port_line="$port_line, TCP: $tcp_interval"
else
port_line="$port_line TCP: $tcp_interval"
fi
port_interval_total=$((port_interval_total + tcp_interval))
port_overall_total=$((port_overall_total + tcp_total))
interval_counts[tcp_$port]=0
fi
port_line="$port_line packets in last ${INTERVAL}s (Total: $port_overall_total)"
echo "$port_line"
interval_total=$((interval_total + port_interval_total))
overall_total=$((overall_total + port_overall_total))
done
echo " TOTAL: $interval_total packets in last ${INTERVAL}s (Total: $overall_total)"
echo ""
last_report=$current_time
unset ports_seen
fi
done
cleanup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment