I find this script useful for debugging which processes are talking to which remote port.
#!/bin/bash
# Network Connection Monitor with Color Coding
# Shows TCP/UDP connections with state and process info
# Refreshes every 5 seconds
# Usage: ./netmon.sh [--port PORT] [--ip IP_ADDRESS]
# Parse command line arguments
FILTER_PORT=""
FILTER_IP=""
while [[ $# -gt 0 ]]; do
case $1 in
--port|-p)
FILTER_PORT="$2"
shift 2
;;
--ip|-i)
FILTER_IP="$2"
shift 2
;;
--help|-h)
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " --port, -p PORT Filter by remote port"
echo " --ip, -i IP Filter by remote IP address"
echo " --help, -h Show this help message"
echo ""
echo "Examples:"
echo " $0 --port 443 Show only connections to port 443"
echo " $0 --ip 1.1.1.1 Show only connections to IP 1.1.1.1"
echo " $0 -p 80 -i 192.168.1.1 Show connections to 192.168.1.1:80"
exit 0
;;
*)
echo "Unknown option: $1"
echo "Use --help for usage information"
exit 1
;;
esac
done
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
MAGENTA='\033[0;35m'
CYAN='\033[0;36m'
WHITE='\033[1;37m'
GRAY='\033[0;90m'
NC='\033[0m' # No Color
BOLD='\033[1m'
# Function to get process name from PID
get_process_name() {
local pid=$1
if [ "$pid" != "-" ] && [ "$pid" != "0" ] && [ -n "$pid" ]; then
ps -p "$pid" -o comm= 2>/dev/null || echo "unknown"
else
echo "-"
fi
}
# Function to color-code based on state
get_state_color() {
local state=$1
case "$state" in
"ESTABLISHED")
echo "${GREEN}"
;;
"LISTEN")
echo "${BLUE}"
;;
"TIME_WAIT")
echo "${YELLOW}"
;;
"CLOSE_WAIT")
echo "${MAGENTA}"
;;
"SYN_SENT"|"SYN_RCVD")
echo "${CYAN}"
;;
"FIN_WAIT"*)
echo "${GRAY}"
;;
"CLOSING"|"LAST_ACK")
echo "${RED}"
;;
*)
echo "${WHITE}"
;;
esac
}
# Function to split address into IP and port
split_address() {
local addr=$1
local ip=""
local port=""
if [[ "$addr" == "*"* ]]; then
ip="*"
port="*"
elif [[ "$addr" =~ ^(.+)\.([0-9]+)$ ]]; then
ip="${BASH_REMATCH[1]}"
port="${BASH_REMATCH[2]}"
elif [[ "$addr" =~ ^(.+):([0-9]+)$ ]]; then
# Handle IPv6 format
ip="${BASH_REMATCH[1]}"
port="${BASH_REMATCH[2]}"
else
ip="$addr"
port="-"
fi
echo "$ip|$port"
}
# Function to check if connection matches filters
matches_filter() {
local remote_ip=$1
local remote_port=$2
# Check port filter
if [ -n "$FILTER_PORT" ] && [ "$remote_port" != "$FILTER_PORT" ]; then
return 1
fi
# Check IP filter
if [ -n "$FILTER_IP" ]; then
# Handle partial IP matching
if [[ "$remote_ip" != *"$FILTER_IP"* ]]; then
return 1
fi
fi
return 0
}
# Function to display connections
show_connections() {
clear
# Header
echo -e "${BOLD}${WHITE}=== Network Connections Monitor ===${NC}"
echo -e "${BOLD}${WHITE}$(date '+%Y-%m-%d %H:%M:%S')${NC}"
# Show active filters
if [ -n "$FILTER_PORT" ] || [ -n "$FILTER_IP" ]; then
echo -e "${YELLOW}Active Filters:${NC}"
[ -n "$FILTER_PORT" ] && echo -e " Remote Port: ${BOLD}$FILTER_PORT${NC}"
[ -n "$FILTER_IP" ] && echo -e " Remote IP: ${BOLD}$FILTER_IP${NC}"
fi
echo ""
# Legend
echo -e "${BOLD}Color Legend:${NC}"
echo -e " ${GREEN}●${NC} ESTABLISHED ${BLUE}●${NC} LISTEN ${YELLOW}●${NC} TIME_WAIT"
echo -e " ${CYAN}●${NC} SYN_SENT/RCVD ${MAGENTA}●${NC} CLOSE_WAIT ${RED}●${NC} CLOSING/LAST_ACK"
echo -e " ${GRAY}●${NC} FIN_WAIT ${WHITE}●${NC} OTHER/UDP"
echo ""
# Table header
printf "${BOLD}%-6s %-22s %-22s %-7s %-12s %-8s %-30s${NC}\n" \
"PROTO" "LOCAL ADDRESS" "REMOTE IP" "R.PORT" "STATE" "PID" "PROCESS"
echo "$(printf '%.0s-' {1..120})"
# Temporary file for storing connections
TMPFILE=$(mktemp)
# Get TCP connections with netstat
# Note: On macOS, we need sudo to see process info for all connections
if command -v sudo >/dev/null 2>&1; then
# Try with sudo first (will show all processes)
sudo netstat -anp tcp 2>/dev/null | grep -E '^tcp' > "$TMPFILE" 2>/dev/null || \
netstat -an -p tcp 2>/dev/null | grep -E '^tcp' > "$TMPFILE"
else
netstat -an -p tcp 2>/dev/null | grep -E '^tcp' > "$TMPFILE"
fi
# Process TCP connections
while IFS= read -r line; do
# Parse netstat output (macOS format)
proto=$(echo "$line" | awk '{print $1}')
local_addr=$(echo "$line" | awk '{print $4}')
remote_addr=$(echo "$line" | awk '{print $5}')
state=$(echo "$line" | awk '{print $6}')
# Split remote address into IP and port
IFS='|' read -r remote_ip remote_port <<< "$(split_address "$remote_addr")"
# Apply filters
if ! matches_filter "$remote_ip" "$remote_port"; then
continue
fi
# Try to get PID using lsof for the local address
if [[ "$local_addr" =~ ([0-9.]+|[*])\.([0-9]+)$ ]] || [[ "$local_addr" =~ ([0-9a-f:]+)\.([0-9]+)$ ]]; then
port="${BASH_REMATCH[2]}"
# Use lsof to find the PID
pid=$(sudo lsof -i TCP:$port -sTCP:$state 2>/dev/null | grep -v PID | head -1 | awk '{print $2}')
if [ -z "$pid" ]; then
pid="-"
process="-"
else
process=$(get_process_name "$pid")
fi
else
pid="-"
process="-"
fi
# Get color based on state
color=$(get_state_color "$state")
# Format and print
printf "${color}%-6s %-22s %-22s %-7s %-12s %-8s %-30s${NC}\n" \
"$proto" \
"${local_addr:0:22}" \
"${remote_ip:0:22}" \
"${remote_port:0:7}" \
"$state" \
"$pid" \
"${process:0:30}"
done < "$TMPFILE"
# Get UDP connections
echo ""
if command -v sudo >/dev/null 2>&1; then
sudo netstat -anp udp 2>/dev/null | grep -E '^udp' > "$TMPFILE" 2>/dev/null || \
netstat -an -p udp 2>/dev/null | grep -E '^udp' > "$TMPFILE"
else
netstat -an -p udp 2>/dev/null | grep -E '^udp' > "$TMPFILE"
fi
# Process UDP connections
while IFS= read -r line; do
# Parse netstat output for UDP
proto=$(echo "$line" | awk '{print $1}')
local_addr=$(echo "$line" | awk '{print $4}')
remote_addr=$(echo "$line" | awk '{print $5}')
# Split remote address into IP and port
IFS='|' read -r remote_ip remote_port <<< "$(split_address "$remote_addr")"
# Apply filters
if ! matches_filter "$remote_ip" "$remote_port"; then
continue
fi
# UDP doesn't have state
state="*"
# Try to get PID using lsof for the local address
if [[ "$local_addr" =~ ([0-9.]+|[*])\.([0-9]+)$ ]] || [[ "$local_addr" =~ ([0-9a-f:]+)\.([0-9]+)$ ]]; then
port="${BASH_REMATCH[2]}"
# Use lsof to find the PID
pid=$(sudo lsof -i UDP:$port 2>/dev/null | grep -v PID | head -1 | awk '{print $2}')
if [ -z "$pid" ]; then
pid="-"
process="-"
else
process=$(get_process_name "$pid")
fi
else
pid="-"
process="-"
fi
# White color for UDP
printf "${WHITE}%-6s %-22s %-22s %-7s %-12s %-8s %-30s${NC}\n" \
"$proto" \
"${local_addr:0:22}" \
"${remote_ip:0:22}" \
"${remote_port:0:7}" \
"$state" \
"$pid" \
"${process:0:30}"
done < "$TMPFILE"
# Clean up
rm -f "$TMPFILE"
# Footer
echo ""
echo "$(printf '%.0s-' {1..120})"
echo -e "${BOLD}Press Ctrl+C to exit${NC} | Refreshing every 5 seconds..."
# Show filter hint if no filters active
if [ -z "$FILTER_PORT" ] && [ -z "$FILTER_IP" ]; then
echo -e "${GRAY}Tip: Use --port PORT or --ip IP to filter connections${NC}"
fi
}
# Trap Ctrl+C to exit cleanly
trap 'echo -e "\n${BOLD}Exiting...${NC}"; exit 0' INT
# Main loop
echo -e "${BOLD}${CYAN}Starting Network Connection Monitor...${NC}"
echo -e "${YELLOW}Note: Run with sudo for complete process information${NC}"
# Show active filters on startup
if [ -n "$FILTER_PORT" ] || [ -n "$FILTER_IP" ]; then
echo -e "${GREEN}Filtering enabled:${NC}"
[ -n "$FILTER_PORT" ] && echo -e " Remote Port: ${BOLD}$FILTER_PORT${NC}"
[ -n "$FILTER_IP" ] && echo -e " Remote IP: ${BOLD}$FILTER_IP${NC}"
fi
sleep 2
while true; do
show_connections
sleep 5
done