วิธีการตรวจสอบเวลาเชื่อมต่อซ็อกเก็ตบน Linux


24

ฉันสามารถตรวจสอบได้ว่าการเชื่อมต่อเกิดขึ้น:

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

มีวิธีการตรวจสอบว่าการเชื่อมต่อพอร์ต TCP นี้ขึ้น (เชื่อมต่อ) นานแค่ไหน?

(ไม่ฉันไม่สามารถเข้าถึงบันทึกแอพ)

คำตอบ:


23

คุณสามารถลองต่อไปนี้:

  1. ได้รับ PID (พูด$pid) ของโปรแกรมโดยการเพิ่มตัวเลือกในการ-pnetstat

  2. ระบุบรรทัดที่เหมาะสมใน/proc/net/tcpไฟล์โดยดูที่local_addressและ / หรือrem_addressฟิลด์ (โปรดทราบว่าพวกเขาอยู่ในรูปแบบเลขฐานสิบหกโดยเฉพาะที่อยู่ IP จะแสดงตามลำดับไบต์แบบ end-endian) ตรวจสอบให้แน่ใจด้วยว่าstเป็น01(สำหรับESTABLISHED)

  3. บันทึกinodeฟิลด์ที่เกี่ยวข้อง(พูด$inode);

  4. ค้นหาสิ่งนั้นinodeท่ามกลางไฟล์ descriptors /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")
}

(แก้ไขขอบคุณAlexสำหรับการแก้ไข )

ตัวอย่าง:

$ suptime 93.184.216.34 80
Thu Dec 24 16:22:58 CET 2015 (46.12s ago)

1
สูตรนี้แสดงอายุของกระบวนการที่สร้างการเชื่อมต่อ TCP ไม่ใช่การเชื่อมต่อเอง
myroslav

@myroslav คุณแน่ใจหรือไม่ มันทำงานกับสคริปต์ Node.jsนี้
cYrus

ฉันจะทดสอบสคริปต์ใหม่ของคุณด้วยการเชื่อมต่อ TCP ที่เปิดโดย Firefox ของฉันบน Fedora 22 64 บิตและฉันไม่ได้รับหมายเลข "สถานะ" เมื่อซ็อกเก็ตใหม่เปิดขึ้นแสดงว่ามีช่วงเวลาที่ "สุ่ม" โดยปกติจะเป็นช่วงเวลาของซ็อกเก็ต "อายุน้อยที่สุด" ที่ถูกสร้างขึ้น
myroslav

@myroslav ฉันใช้ Debian (3.16.0-4-amd64) ที่นี่สิ่งเดียวที่ฉันสังเกตเห็นคือเวลาที่รายงานนั้นจริง ๆ แล้วประมาณ 3 วินาทีที่เกี่ยวกับการสร้างซ็อกเก็ต อาจมีพฤติกรรมบางอย่างที่เกี่ยวข้องกับระบบที่เกี่ยวข้อง ...
cYrus

สำหรับสคริปต์ "$ suptime 192: 168: 120: 10 6379 การติดตามย้อนกลับ (การโทรล่าสุดครั้งล่าสุด): ไฟล์" <string> "บรรทัดที่ 1 ในซ็อกเก็ต <module>.error: สตริงที่อยู่ IP ผิดกฎหมายที่ส่งผ่านไปยังที่อยู่ inet_aton ไม่ตรง "
Ondra Žižka

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 - ไม่มีส่วนหัว -o'pid ให้เริ่มต้น '-p

ของหลักสูตรนี้ถือว่าซ็อกเก็ตเริ่มต้นเมื่อกระบวนการ


สิ่งนี้แสดงระยะเวลาที่กระบวนการเปิดซ็อกเก็ตขึ้น ในกรณีที่มีกระบวนการที่ทำงานตลอดเวลาและมีการตัดการเชื่อมต่อเครือข่ายค่าเหล่านี้จะแตกต่างกันมาก +1 สำหรับความพยายาม
hidralisk

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 และระบบที่คล้ายกัน)
  • จำเป็นต้องเรียกใช้ในฐานะรูท
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.