36

I can verify that the connection is up:

$ netstat -tn | grep "192.168.2.110"
tcp  0  0 192.168.2.100:10444  192.168.2.110:52639  ESTABLISHED

is there a way to check how long this tcp port connection was up (connected)?

(No, I don't have access to app logs)

6 Answers 6

35

You can try the following:

  1. get the PID (say $pid) of the program by adding the -p option to netstat.

  2. identify the proper line in the /proc/net/tcp file by looking at the local_address and/or rem_address fields (note that they are in hex format, specifically the IP address is expressed in little-endian byte order), also make sure that the st is 01 (for ESTABLISHED);

  3. note the associated inode field (say $inode);

  4. search for that inode among the file descriptors in /proc/$pid/fd and finally query the file access time of the symbolic link:

    find /proc/$pid/fd -lname "socket:\[$inode\]" -printf %t
    

That is a grunt work... here's a script (stub) to automatize the above points, it requires the remote address and it prints the socket uptime in seconds:

function suptime() {
    local addr=${1:?Specify the remote IPv4 address}
    local port=${2:?Specify the remote port number}
    # convert the provided address to hex format
    local hex_addr=$(python -c "import socket, struct; print(hex(struct.unpack('<L', socket.inet_aton('$addr'))[0])[2:10].upper().zfill(8))")
    local hex_port=$(python -c "print(hex($port)[2:].upper().zfill(4))")
    # get the PID of the owner process
    local pid=$(netstat -ntp 2>/dev/null | awk '$6 == "ESTABLISHED" && $5 == "'$addr:$port'"{sub("/.*", "", $7); print $7}')
    [ -z "$pid" ] && { echo 'Address does not match' 2>&1; return 1; }
    # get the inode of the socket
    local inode=$(awk '$4 == "01" && $3 == "'$hex_addr:$hex_port'" {print $10}' /proc/net/tcp)
    [ -z "$inode" ] && { echo 'Cannot lookup the socket' 2>&1; return 1; }
    # query the inode status change time
    local timestamp=$(find /proc/$pid/fd -lname "socket:\[$inode\]" -printf %T@)
    [ -z "$timestamp" ] && { echo 'Cannot fetch the timestamp' 2>&1; return 1; }
    # compute the time difference
    LANG=C printf '%s (%.2fs ago)\n' "$(date -d @$timestamp)" $(bc <<<"$(date +%s.%N) - $timestamp")
}

(Edit thanks to Alex for the fixes)

Example:

$ suptime 93.184.216.34 80
Thu Dec 24 16:22:58 CET 2015 (46.12s ago)
9
  • 2
    This recipe displays age of process that created TCP connection, not connection itself.
    – myroslav
    Dec 24, 2015 at 13:17
  • @myroslav are you sure? It works against this Node.js script.
    – cYrus
    Dec 24, 2015 at 15:33
  • I'd tested your new script with TCP connections opened by my Firefox on Fedora 22 64-bit, and I'm getting definitely not "uptime" numbers. When new socket opens, it is getting "random" uptime, usually the time of "youngest" ESTABLISHED socket.
    – myroslav
    Dec 25, 2015 at 23:23
  • @myroslav I'm using Debian (3.16.0-4-amd64) here, the only thing I notice is that the time reported is actually about 3 seconds late with respect to the socket creation. Maybe there are some system-dependent behaviors involved...
    – cYrus
    Dec 26, 2015 at 11:21
  • 2
    This works but it's not accurate: the /proc/$pid/fd/$fd entry is not created when the socket itself is created, but only on the first time this /proc/$pid/fd directory is being listed (testing on Linux 5.4.0). If you want it to be accurate you'd have to list the directory periodically.
    – Jong
    May 13, 2020 at 9:12
6

This questions was helpful to me, but I found using lsof instead of netstat let me avoid all the HEX stuff:

For a process ${APP} run by user ${USER}, the following returns all the open sockets to the IP address ${IP}:

PEEID=$(sudo pgrep -u ${USER} ${APP}) && for i in `sudo lsof -anP -i -u logstash | grep ${IP} | awk '{print $6}'` ; do echo "${device} time" ; sudo find /proc/${PEEID}/fd -lname "socket:\[${device}\]" -printf %t 2> /dev/null  ; echo  ;  done

The lsof contains the PID too, but I am not sure how to get it and the device number.

This was tested on Amazon Linux.

1
  • Since lsof shows fd, you don't really need the find part but just check the fd time
    – Louis Go
    Nov 3, 2023 at 3:38
3

The script by cYrus worked for me but i had to fix it a bit (to get rid of a "L" in the hex address and to make port a 4 digit hex):

--- suptime.orig    2015-08-20 15:46:12.896652464 +0200
+++ suptime 2015-08-20 15:47:48.560074728 +0200
@@ -7,8 +7,8 @@
     hex_addr=$(python -c "
 import socket, struct;
 print hex(struct.unpack('<L',
-socket.inet_aton('$addr'))[0])[2:].upper().zfill(8)")
-    hex_port=$(python -c "print hex($port)[2:].upper()")
+socket.inet_aton('$addr'))[0])[2:10].upper().zfill(8)")
+    hex_port=$(python -c "print hex($port)[2:].upper().zfill(4)")
     inode=$(awk '$3 == "'$hex_addr:$hex_port'" {print $10}' /proc/net/tcp)
     time=$(find /proc/$pid/fd -lname "socket:\[$inode\]" -printf %A@)
     LANG=C printf '%.2fs' $(bc <<<"$(date +%s.%N) - $time")
1

How about:

lsof -t -i @192.168.2.110 | xargs ps -fp

You can also tailor the "ps" command to just get pid and start time with -o such as:

lsof -t -i @192.168.2.110 | xargs ps --no-headers -o'pid,start' -p

Of course this assumes the socket was started when the process was.

1
  • 1
    this shows how long the process that opened the socket is up. In case there is a process that runs all the time and there are network disconnects, these values would be very different. +1 for effort
    – hidralisk
    Jun 26, 2019 at 23:13
1

Thanks for the script maintained in cYrus' answer. I had issues with it printing duplicates, probably because there may be many connections from different PIDs to the provided address, so here's my improved version that also prints the PID on each output line:

function suptime() {
    local addr=${1:?Specify the remote IPv4 address}
    local port=${2:?Specify the remote port number}

    # convert the provided address to hex format
    local hex_addr=$(python -c "import socket, struct; print(hex(struct.unpack('<L', socket.inet_aton('$addr'))[0])[2:10].upper().zfill(8))")
    local hex_port=$(python -c "print(hex($port)[2:].upper().zfill(4))")

    # get the inode of the socket
    local inodes=$(awk '$4 == "01" && $3 == "'$hex_addr:$hex_port'" {print $10}' /proc/net/tcp)
    [ -z "$inodes" ] && { echo 'Cannot lookup the socket(s)' 2>&1; return 1; }

    # get file descriptors
    for inode in $inodes; do
        # get inode's file descriptor details
        local fdinfo=( $(find /proc/[0-9]*/fd -lname "socket:\[$inode\]" -printf "%p %T@") )
        [ -z "$fdinfo" ] && { echo 'Cannot find file descriptor' 2>&1; return 1; }

        # extract pid
        local fdpath=${fdinfo[0]}
        local pid=${fdpath#/proc/}
        pid=${pid%%/*}

        # extract timestamp
        local timestamp=${fdinfo[1]}

        # compute the time difference
        LANG=C printf 'PID: %s; Age: %s (%.2fs ago)\n' "$pid" "$(date -d @$timestamp)" $(bc <<<"$(date +%s.%N) - $timestamp")
    done
}

Notes:

  • needs bc, netstat (provided by net-tools on rhel >= 7 and similar systems)
  • needs to be run as root
0

A different version/form of Raúl Cuza's script, with some fixed bugs, that is (arguably) more usable, and which prints a total connections count:

APP=your-app
USER=user-app-runs-under
IP=ip-address-in-question
PID=$(sudo pgrep -u ${USER} ${APP})
CONNECTIONS=$(sudo lsof -anP -i -u ${USER} | grep ${IP} | awk '{print $6}')
echo "total ${USER} user connections to ${IP}: $(echo ${CONNECTIONS} | wc -w)"
for device in ${CONNECTIONS}; do
    timestamp=$(sudo find /proc/${PID}/fd -lname "socket:\[${device}\]" -printf %T@ 2> /dev/null);
    echo "${device} time";
    # compute the time difference
    LANG=C printf '%s (%.2fs ago)\n' "$(date -d @$timestamp)" $(bc <<<"$(date +%s.%N) - $timestamp");
    echo;
done

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .