참고 : 이제 lsof
여기에 설명 된 두 가지 방법을 결합하고 https://github.com/stephane-chazelas/misc-scripts/blob/master/lsofc 에서 루프백 TCP 연결 피어에 대한 정보를 추가 하는 래퍼를 유지 관리합니다.
Linux-3.3 이상
Linux에서는 커널 버전 3.3 (및 UNIX_DIAG
기능이 커널에 내장되어있는 경우)이므로 새로운 netlink 기반 API를 사용하여 지정된 유닉스 도메인 소켓 (소켓 쌍 포함)의 피어를 얻을 수 있습니다 .
lsof
버전 4.89가 해당 API를 사용할 수 있기 때문에 :
lsof +E -aUc Xorg
프로세스의 이름이 Xorg
양쪽 끝에서 시작하는 프로세스가있는 모든 유닉스 도메인 소켓 을 다음과 유사한 형식으로 나열합니다 .
Xorg 2777 root 56u unix 0xffff8802419a7c00 0t0 34036 @/tmp/.X11-unix/X0 type=STREAM ->INO=33273 4120,xterm,3u
버전 lsof
이 너무 오래된 경우 몇 가지 옵션이 더 있습니다.
ss
(에서 유틸리티 iproute2
) 검색하고 피어 정보를 포함하여 시스템에 유닉스 도메인 소켓의 목록에 정보를 표시하는 동일한 API를 사용합니다.
소켓은 inode 번호 로 식별됩니다 . 소켓 파일의 파일 시스템 inode와 관련이 없습니다.
예를 들어
$ ss -x
[...]
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 3435997 * 3435996
그것은 소켓 3435997 (ABSTRACT 소켓에 바인드 됨 /tmp/.X11-unix/X0
)이 소켓 3435996에 연결되어 있다고 말합니다.이 -p
옵션은 소켓이 열린 프로세스를 알려줍니다. 그것은 수행하여 어떤 것을 수행 readlink
에 대한이야 /proc/$pid/fd/*
, 그래서 (당신이 아니라면 그것은 단지 당신이 자신의 프로세스에서 해당 작업을 수행 할 수 있습니다 root
). 예를 들면 다음과 같습니다.
$ sudo ss -xp
[...]
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 3435997 * 3435996 users:(("Xorg",pid=3080,fd=83))
[...]
$ sudo ls -l /proc/3080/fd/23
lrwx------ 1 root root 64 Mar 12 16:34 /proc/3080/fd/83 -> socket:[3435997]
3435996이있는 프로세스를 찾으려면 다음과 같은 출력에서 자체 항목을 찾아 볼 수 있습니다 ss -xp
.
$ ss -xp | awk '$6 == 3435996'
u_str ESTAB 0 0 * 3435996 * 3435997 users:(("xterm",pid=29215,fd=3))
이 스크립트를 래퍼로 사용 lsof
하여 관련 정보를 쉽게 표시 할 수도 있습니다 .
#! /usr/bin/perl
# lsof wrapper to add peer information for unix domain socket.
# Needs Linux 3.3 or above and CONFIG_UNIX_DIAG enabled.
# retrieve peer and direction information from ss
my (%peer, %dir);
open SS, '-|', 'ss', '-nexa';
while (<SS>) {
if (/\s(\d+)\s+\*\s+(\d+) ([<-]-[->])$/) {
$peer{$1} = $2;
$dir{$1} = $3;
}
}
close SS;
# Now get info about processes tied to sockets using lsof
my (%fields, %proc);
open LSOF, '-|', 'lsof', '-nPUFpcfin';
while (<LSOF>) {
if (/(.)(.*)/) {
$fields{$1} = $2;
if ($1 eq 'n') {
$proc{$fields{i}}->{"$fields{c},$fields{p}" .
($fields{n} =~ m{^([@/].*?)( type=\w+)?$} ? ",$1" : "")} = "";
}
}
}
close LSOF;
# and finally process the lsof output
open LSOF, '-|', 'lsof', @ARGV;
while (<LSOF>) {
chomp;
if (/\sunix\s+\S+\s+\S+\s+(\d+)\s/) {
my $peer = $peer{$1};
if (defined($peer)) {
$_ .= $peer ?
" ${dir{$1}} $peer\[" . (join("|", keys%{$proc{$peer}})||"?") . "]" :
"[LISTENING]";
}
}
print "$_\n";
}
close LSOF or exit(1);
예를 들면 다음과 같습니다.
$ sudo that-lsof-wrapper -ad3 -p 29215
명령 PID 사용자 FD 유형 장치 크기 / 꺼짐 노드 이름
xterm 29215 스테판 3u 유닉스 0xffff8800a07da4c0 0t0 3435996 type = STREAM <-> 3435997 [Xorg, 3080, @ / tmp / .X11-unix / X0]
리눅스 3.3 이전
유닉스 소켓 정보를 검색하는 이전 Linux API는 /proc/net/unix
텍스트 파일을 통해 이루어 집니다. 모든 Unix 도메인 소켓 (소켓 쌍 포함)을 나열합니다. @Totor 에 의해 이미 설명 된kernel.kptr_restrict
것처럼 거기에있는 첫 번째 필드 ( sysctl 매개 변수를 사용하여 비 수퍼 유저에게 숨겨져 있지 않은 경우 ) 에는 해당 피어를 가리키는 필드 를 포함하는 구조 의 커널 주소 가 포함 됩니다. 또한 Unix 소켓 의 열에 대한 출력입니다 .unix_sock
peer
unix_sock
lsof
DEVICE
이제 해당 peer
필드 의 값을 얻는다는 것은 커널 메모리를 읽고 주소 peer
와 관련하여 해당 필드 의 오프셋을 알 수 있다는 것을 의미 unix_sock
합니다.
여러 gdb
기반 및 systemtap
기반 솔루션이 이미 제공되었지만 일반적으로 프로덕션 시스템에서는 그렇지 않은 설치중인 커널에 대해 gdb
/ systemtap
및 Linux 커널 디버그 기호가 필요합니다.
커널 버전에 따라 오프셋을 하드 코딩하는 것은 실제로 옵션이 아닙니다.
이제 오프셋을 결정할 때 휴리스틱 접근 방식을 사용할 수 있습니다. 도구에서 더미를 만들고 socketpair
(두 피어의 주소를 모두 알고 있음) 다른 쪽 끝에서 메모리 주변의 피어 주소를 검색 하여 오프셋을 결정합니다.
다음은이를 사용하는 개념 증명 스크립트입니다 perl
(i386에서 커널 2.4.27 및 2.6.32 및 amd64에서 3.13 및 3.16으로 성공적으로 테스트 됨). 위와 같이 래퍼로 작동합니다 lsof
.
예를 들면 다음과 같습니다.
$ that-lsof-wrapper -aUc nm- 애플릿
명령 PID 사용자 FD 유형 장치 크기 / 꺼짐 노드 이름
NM-애플릿 4183 스테판의 4U UNIX 0xffff8800a055eb40 0t0 36,888 TYPE = STREAM -> 0xffff8800a055e7c0 [DBUS 데몬, 4190 @ / TMP / DBUS-AiBCXOnuP6]
nm의 애플릿 4183 스테판의 7U UNIX 0xffff8800a055e440 0t0 36,890 TYPE = STREAM -> 0xffff8800a055e0c0 [이 xorg 3080, @ / tmp / .X11-unix / X0]
nm-applet 4183 스테판 8u 유닉스 0xffff8800a05c1040 0t0 36201 type = STREAM-> 0xffff8800a05c13c0 [dbus-daemon, 4118, @ / tmp / dbus-yxxNr1NkYC]
nm-applet 4183 UNIX 0xffff8800a055d080 0t0 36,219 TYPE = STREAM -> 0xffff8800a055d400 [DBUS 데몬, 4118 @ / TMP / DBUS-yxxNr1NkYC]
nm의 애플릿 4183 UNIX 0xffff88022e0dfb80 0t0 36,221 TYPE = STREAM 12U 스테판 -> 0xffff88022e0df800 [DBUS 데몬, 2,268 / VAR / run / dbus / system_bus_socket]
nm-applet 4183 스테판 13u 유닉스 0xffff88022e0f80c0 0t0 37025 type = STREAM-> 0xffff88022e29ec00 [dbus-daemon, 2268, / var / run / dbus / system_bus_socket]
스크립트는 다음과 같습니다.
#! /usr/bin/perl
# wrapper around lsof to add peer information for Unix
# domain sockets. needs lsof, and superuser privileges.
# Copyright Stephane Chazelas 2015, public domain.
# example: sudo this-lsof-wrapper -aUc Xorg
use Socket;
open K, "<", "/proc/kcore" or die "open kcore: $!";
read K, $h, 8192 # should be more than enough
or die "read kcore: $!";
# parse ELF header
my ($t,$o,$n) = unpack("x4Cx[C19L!]L!x[L!C8]S", $h);
$t = $t == 1 ? "L3x4Lx12" : "Lx4QQx8Qx16"; # program header ELF32 or ELF64
my @headers = unpack("x$o($t)$n",$h);
# read data from kcore at given address (obtaining file offset from ELF
# @headers)
sub readaddr {
my @h = @headers;
my ($addr, $length) = @_;
my $offset;
while (my ($t, $o, $v, $s) = splice @h, 0, 4) {
if ($addr >= $v && $addr < $v + $s) {
$offset = $o + $addr - $v;
if ($addr + $length - $v > $s) {
$length = $s - ($addr - $v);
}
last;
}
}
return undef unless defined($offset);
seek K, $offset, 0 or die "seek kcore: $!";
my $ret;
read K, $ret, $length or die "read($length) kcore \@$offset: $!";
return $ret;
}
# create a dummy socketpair to try find the offset in the
# kernel structure
socketpair(Rdr, Wtr, AF_UNIX, SOCK_STREAM, PF_UNSPEC)
or die "socketpair: $!";
$r = readlink("/proc/self/fd/" . fileno(Rdr)) or die "readlink Rdr: $!";
$r =~ /\[(\d+)/; $r = $1;
$w = readlink("/proc/self/fd/" . fileno(Wtr)) or die "readlink Wtr: $!";
$w =~ /\[(\d+)/; $w = $1;
# now $r and $w contain the socket inodes of both ends of the socketpair
die "Can't determine peer offset" unless $r && $w;
# get the inode->address mapping
open U, "<", "/proc/net/unix" or die "open unix: $!";
while (<U>) {
if (/^([0-9a-f]+):(?:\s+\S+){5}\s+(\d+)/) {
$addr{$2} = hex $1;
}
}
close U;
die "Can't determine peer offset" unless $addr{$r} && $addr{$w};
# read 2048 bytes starting at the address of Rdr and hope to find
# the address of Wtr referenced somewhere in there.
$around = readaddr $addr{$r}, 2048;
my $offset = 0;
my $ptr_size = length(pack("L!",0));
my $found;
for (unpack("L!*", $around)) {
if ($_ == $addr{$w}) {
$found = 1;
last;
}
$offset += $ptr_size;
}
die "Can't determine peer offset" unless $found;
my %peer;
# now retrieve peer for each socket
for my $inode (keys %addr) {
$peer{$addr{$inode}} = unpack("L!", readaddr($addr{$inode}+$offset,$ptr_size));
}
close K;
# Now get info about processes tied to sockets using lsof
my (%fields, %proc);
open LSOF, '-|', 'lsof', '-nPUFpcfdn';
while (<LSOF>) {
if (/(.)(.*)/) {
$fields{$1} = $2;
if ($1 eq 'n') {
$proc{hex($fields{d})}->{"$fields{c},$fields{p}" .
($fields{n} =~ m{^([@/].*?)( type=\w+)?$} ? ",$1" : "")} = "";
}
}
}
close LSOF;
# and finally process the lsof output
open LSOF, '-|', 'lsof', @ARGV;
while (<LSOF>) {
chomp;
for my $addr (/0x[0-9a-f]+/g) {
$addr = hex $addr;
my $peer = $peer{$addr};
if (defined($peer)) {
$_ .= $peer ?
sprintf(" -> 0x%x[", $peer) . join("|", keys%{$proc{$peer}}) . "]" :
"[LISTENING]";
last;
}
}
print "$_\n";
}
close LSOF or exit(1);