Как определить время подключения сокета в Linux

24

Я могу убедиться, что соединение установлено:

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

Есть ли способ проверить, как долго это соединение порта TCP был подключен?

(Нет, у меня нет доступа к журналам приложений)

hidralisk
источник

Ответы:

23

Вы можете попробовать следующее:

  1. получить PID (скажем $pid) программы, добавив -pопцию к netstat.

  2. определите правильную строку в /proc/net/tcpфайле, просматривая поля local_addressи / или rem_address(обратите внимание, что они представлены в шестнадцатеричном формате, в частности, IP-адрес выражается в порядке байтов с прямым порядком байтов), также убедитесь, что stis 01(for ESTABLISHED);

  3. обратите внимание на соответствующее inodeполе (скажем $inode);

  4. найдите его inodeсреди дескрипторов файлов /proc/$pid/fdи, наконец, запросите время доступа к файлу символической ссылки:

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

Это тяжелая работа ... вот скрипт (заглушка) для автоматизации вышеупомянутых пунктов, он требует удаленного адреса и печатает время работы сокета в секундах:

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

(Изменить спасибо Алексу за исправления )

Пример:

$ suptime 93.184.216.34 80
Thu Dec 24 16:22:58 CET 2015 (46.12s ago)
Кир
источник
1
Этот рецепт отображает возраст процесса создания TCP-соединения, а не самого соединения.
Мирослав
@myroslav ты уверен? Он работает против этого сценария Node.js .
cYrus
Я протестировал ваш новый сценарий с TCP-соединениями, открытыми моим Firefox на 64-битной Fedora 22, и я определенно не получаю числа «безотказной работы». Когда открывается новый сокет, он получает «случайное» время безотказной работы, обычно это время «самого младшего» установленного сокета.
Мирослав
@myroslav Я использую Debian (3.16.0-4-amd64) здесь, единственное, что я замечаю, это то, что указанное время на самом деле составляет около 3 секунд с опозданием на создание сокета. Может быть, это связано с системно-зависимым поведением ...
cYrus
Для сценария «$ suptime 192: 168: 120: 10 6379 Traceback (последний вызов был последним): файл« <string> », строка 1, в <module> socket.error: недопустимая строка IP-адреса, переданная inet_aton Address не совпадают "
Ондра Жижка
4

Эти вопросы были полезны для меня, но я обнаружил, что lsofвместо того, чтобы netstatпозволять мне избегать всего, что касается HEX , я использовал :

Для процесса, ${APP}запускаемого пользователем ${USER}, следующее возвращает все открытые сокеты на 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

lsofСодержит PIDслишком, но я не знаю , как получить его и номер устройства.

Это было проверено на Amazon Linux.

Рауль Куза
источник
3

Сценарий cYrus работал для меня, но мне пришлось немного его исправить (чтобы избавиться от «L» в шестнадцатеричном адресе и сделать порт четырехзначным шестнадцатеричным):

--- 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

Как насчет:

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

Вы также можете настроить команду "ps", чтобы получить pid и время запуска с помощью -o, например:

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

Конечно, это предполагает, что сокет был запущен во время процесса.

Пит
источник
это показывает, как долго работает процесс, открывший сокет. В случае, если есть процесс, который выполняется все время, и существуют сетевые отключения, эти значения будут очень разными. +1 за усилие
гидралиск
1

Спасибо за скрипт, поддерживаемый в ответе cYrus. У меня были проблемы с печатью дубликатов, возможно потому, что к указанному адресу может быть много подключений из разных PID, поэтому вот моя улучшенная версия, которая также печатает PID в каждой строке вывода:

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
}

Примечания:

  • потребности bc, netstat(предоставлено net-toolsна rhel> = 7 и аналогичных системах)
  • должен быть запущен от имени пользователя root
fholzer
источник