Hinweis : Ich verwalte jetzt einen lsof
Wrapper, der beide hier beschriebenen Ansätze kombiniert und Informationen für Peers von Loopback-TCP-Verbindungen unter https://github.com/stephane-chazelas/misc-scripts/blob/master/lsofc hinzufügt
Linux-3.3 und höher.
Unter Linux kann seit Kernel-Version 3.3 (und vorausgesetzt, die UNIX_DIAG
Funktion ist im Kernel integriert) der Peer eines bestimmten Unix-Domain-Sockets (einschließlich Socket-Paaren) mithilfe einer neuen netlink- basierten API abgerufen werden .
lsof
Seit Version 4.89 kann diese API verwendet werden:
lsof +E -aUc Xorg
Listet alle Unix-Domain-Sockets mit einem Prozess auf, dessen Name Xorg
an beiden Enden in einem Format beginnt, das etwa wie folgt lautet :
Xorg 2777 root 56u unix 0xffff8802419a7c00 0t0 34036 @/tmp/.X11-unix/X0 type=STREAM ->INO=33273 4120,xterm,3u
Wenn Ihre Version von lsof
zu alt ist, gibt es einige weitere Optionen.
Das ss
Dienstprogramm (from iproute2
) verwendet dieselbe API zum Abrufen und Anzeigen von Informationen in der Liste der Unix-Domain-Sockets auf dem System, einschließlich Peer-Informationen.
Die Sockets werden anhand ihrer Inode-Nummer identifiziert . Beachten Sie, dass es nicht mit dem Dateisystem-Inode der Socket-Datei zusammenhängt.
Zum Beispiel in:
$ ss -x
[...]
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 3435997 * 3435996
Es heißt, dass der Socket 3435997 (der an den ABSTRACT-Socket gebunden war /tmp/.X11-unix/X0
) mit dem Socket 3435996 verbunden ist. Die -p
Option gibt an, bei welchen Prozessen dieser Socket geöffnet ist. Dies geschieht durch einige readlink
s on /proc/$pid/fd/*
, so dass dies nur bei Prozessen möglich ist, die Sie besitzen (es sei denn, Sie sind root
). Zum Beispiel hier:
$ 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]
Um herauszufinden, welche Prozesse 3435996 haben, können Sie einen eigenen Eintrag in der Ausgabe von ss -xp
:
$ ss -xp | awk '$6 == 3435996'
u_str ESTAB 0 0 * 3435996 * 3435997 users:(("xterm",pid=29215,fd=3))
Sie können dieses Skript auch als Wrapper verwenden, um lsof
die relevanten Informationen dort einfach anzuzeigen:
#! /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);
Zum Beispiel:
$ sudo that-lsof-wrapper -ad3 -p 29215
BEFEHL PID BENUTZER FD TYP GERÄTEGRÖSSE / OFF NODE NAME
xterm 29215 stephane 3u unix 0xffff8800a07da4c0 0t0 3435996 type = STREAM <-> 3435997 [Xorg, 3080, @ / tmp / .X11-unix / X0]
Vor Linux-3.3
Die alte Linux-API zum Abrufen von Unix-Socket-Informationen erfolgt über die /proc/net/unix
Textdatei. Es listet alle Unix-Domain-Sockets (einschließlich Socket-Paare) auf. Das erste Feld dort (falls nicht für Nicht-Superuser mit dem kernel.kptr_restrict
Parameter sysctl ausgeblendet ), wie bereits von @Totor erläutert, enthält die Kernel-Adresse einer unix_sock
Struktur, die ein peer
Feld enthält , das auf den entsprechenden Peer verweist unix_sock
. Dies ist auch die lsof
Ausgabe für die DEVICE
Spalte in einem Unix-Socket.
Wenn Sie nun den Wert dieses peer
Felds ermitteln, können Sie den Kernel-Speicher lesen und den Versatz dieses peer
Felds in Bezug auf die unix_sock
Adresse ermitteln.
Es wurden bereits mehrere gdb
-basierte und systemtap
-basierte Lösungen angegeben, für die jedoch gdb
/ systemtap
und Linux-Kernel-Debugsymbole für den ausgeführten Kernel erforderlich sind, was auf Produktionssystemen im Allgemeinen nicht der Fall ist.
Das Hardcodieren des Offsets ist nicht wirklich eine Option, da dies mit der Kernelversion variiert.
Jetzt können wir den Versatz mit einem heuristischen Ansatz bestimmen: Lassen Sie unser Tool einen Dummy erstellen socketpair
(dann kennen wir die Adresse beider Peers) und suchen Sie nach der Adresse des Peers im Arbeitsspeicher am anderen Ende, um den Versatz zu bestimmen.
Hier ist ein Proof-of-Concept-Skript, das genau das tut perl
(erfolgreich getestet mit Kernel 2.4.27 und 2.6.32 auf i386 und 3.13 und 3.16 auf amd64). Wie oben funktioniert es als Wrapper um lsof
:
Zum Beispiel:
$ that-lsof-wrapper -aUc nm-applet
BEFEHL PID BENUTZER FD TYP GERÄTEGRÖSSE / OFF NODE NAME
nm-applet 4183 stephane 4U UNIX- 0xffff8800a055eb40 0T0 36888 type = STREAM -> 0xffff8800a055e7c0 [dbus-Daemon, 4190, @ / tmp / dbus-AiBCXOnuP6]
nm-applet 4183 stephane 7U UNIX- 0xffff8800a055e440 0T0 36890 type = STREAM -> 0xffff8800a055e0c0 [Xorg, 3080, @ / tmp / .X11-Unix / X0]
nm-applet 4183 stephane 8U UNIX- 0xffff8800a05c1040 0T0 36201 type = STREAM -> 0xffff8800a05c13c0 [dbus-Daemon, 4118, @ / tmp / dbus-yxxNr1NkYC]
nm-applet 4183 stephane 11U unix 0xffff8800a055d080 0t0 36219 type = STREAM -> 0xffff8800a055d400 [dbus-daemon, 4118, @ / tmp / dbus-yxxNr1NkYC]
nm-applet 4183 stephane 12u unix 0xffff88022e0dfb80 0t0 36221 -> / run / dbus / system_bus_socket]
nm-applet 4183 stephane 13u unix 0xffff88022e0f80c0 0t0 37025 type = STREAM -> 0xffff88022e29ec00 [dbus-daemon, 2268, / var / run / dbus / system_bus_socket]
Hier ist das Skript:
#! /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);