Cómo determinar el tiempo de conexión de socket en Linux

24

Puedo verificar que la conexión esté activa:

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

¿Hay alguna manera de verificar cuánto tiempo estuvo activa (conectada) esta conexión de puerto TCP?

(No, no tengo acceso a los registros de la aplicación)

hidralisco
fuente

Respuestas:

23

Puedes probar lo siguiente:

  1. obtenga el PID (digamos $pid) del programa agregando la -popción a netstat.

  2. identifique la línea correcta en el /proc/net/tcparchivo mirando los campos local_addressy / o rem_address(tenga en cuenta que están en formato hexadecimal, específicamente la dirección IP se expresa en orden de bytes little-endian), también asegúrese de que stsea 01(para ESTABLISHED);

  3. tenga en cuenta el inodecampo asociado (por ejemplo $inode);

  4. busque eso inodeentre los descriptores de archivo /proc/$pid/fdy finalmente consulte el tiempo de acceso al archivo del enlace simbólico:

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

Ese es un trabajo duro ... aquí hay un script (stub) para automatizar los puntos anteriores, requiere la dirección remota e imprime el tiempo de actividad del socket en segundos:

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")
}

(Editar gracias a Alex por las correcciones )

Ejemplo:

$ suptime 93.184.216.34 80
Thu Dec 24 16:22:58 CET 2015 (46.12s ago)
Ciro
fuente
1
Esta receta muestra la edad del proceso que creó la conexión TCP, no la conexión en sí.
myroslav
@myroslav ¿estás seguro? Funciona en contra de esta secuencia de comandos Node.js .
cYrus
Probé su nuevo script con conexiones TCP abiertas por mi Firefox en Fedora 22 de 64 bits, y definitivamente no obtengo números de "tiempo de actividad". Cuando se abre un nuevo socket, está obteniendo un tiempo de actividad "aleatorio", generalmente el tiempo del socket ESTABLECIDO "más joven".
myroslav
@myroslav Estoy usando Debian (3.16.0-4-amd64) aquí, lo único que noto es que el tiempo informado es en realidad unos 3 segundos de retraso con respecto a la creación del socket. Tal vez hay algunos comportamientos dependientes del sistema involucrados ...
cYrus
Para el script, "$ suptime 192: 168: 120: 10 6379 Traceback (última llamada más reciente): Archivo" <cadena> ", línea 1, en <módulo> socket.error: cadena de dirección IP ilegal pasada a inet_aton La dirección sí no coincide "
Ondra Žižka
4

Estas preguntas me ayudaron, pero descubrí que usar en lsoflugar de netstatdejarme evitar todas las cosas HEX:

Para un proceso ${APP}ejecutado por el usuario ${USER}, lo siguiente devuelve todos los sockets abiertos a la dirección IP $ {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

El lsofcontiene PIDtambién, pero no estoy seguro de cómo obtenerlo y el número de dispositivo.

Esto fue probado en Amazon Linux.

Raúl Cuza
fuente
3

El script de cYrus funcionó para mí, pero tuve que arreglarlo un poco (para deshacerme de una "L" en la dirección hexadecimal y convertir el puerto en un hexadecimal de 4 dígitos):

--- 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")
Alex Vazquez Fente
fuente
1

Qué tal si:

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

También puede personalizar el comando "ps" para obtener pid y comenzar la hora con -o como:

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

Por supuesto, esto supone que el socket se inició cuando el proceso fue.

Pete
fuente
Esto muestra cuánto tiempo está activo el proceso que abrió el socket. En caso de que haya un proceso que se ejecute todo el tiempo y haya desconexiones de la red, estos valores serían muy diferentes. +1 por esfuerzo
hidralisco
1

Gracias por el guión mantenido en la respuesta de cYrus. Tuve problemas con la impresión de duplicados, probablemente porque puede haber muchas conexiones de diferentes PID a la dirección proporcionada, así que aquí está mi versión mejorada que también imprime el PID en cada línea de salida:

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
}

Notas:

  • necesidades bc, netstat(proporcionado por net-toolsel rhel> = 7 y sistemas similares)
  • necesita ejecutarse como root
Fholzer
fuente