Wer hat das andere Ende dieses Unix-Socketpaars?

54

Ich möchte feststellen, welcher Prozess das andere Ende eines UNIX-Sockets hat.

Insbesondere frage ich nach einem, der mit erstellt wurde socketpair(), obwohl das Problem für alle UNIX-Sockets gleich ist.

Ich habe ein Programm parent, das a socketpair(AF_UNIX, SOCK_STREAM, 0, fds)und fork()s erstellt. Der übergeordnete Prozess wird geschlossen fds[1]und die fds[0]Kommunikation wird fortgesetzt. Das Kind tut das Gegenteil close(fds[0]); s=fds[1]. Dann ist das Kind ein exec()anderes Programm child1,. Über dieses Socketpaar können die beiden miteinander kommunizieren.

Angenommen, ich weiß, wer parentist, aber ich möchte herausfinden, wer child1ist. Wie mache ich das?

Es stehen mehrere Tools zur Verfügung, aber keiner kann mir sagen, welcher Prozess sich am anderen Ende des Sockets befindet. Ich habe versucht:

  • lsof -c progname
  • lsof -c parent -c child1
  • ls -l /proc/$(pidof server)/fd
  • cat /proc/net/unix

Grundsätzlich kann ich die beiden Steckdosen und alles daran sehen, aber nicht sagen, dass sie verbunden sind. Ich versuche festzustellen, welcher FD im Elternteil mit welchem ​​Kindprozeß kommuniziert.

Jonathon Reinhart
quelle

Antworten:

27

Seit Kernel 3.3 ist es möglich, ssoder lsof-4.89oder höher zu verwenden - siehe die Antwort von Stéphane Chazelas .

In älteren Versionen war es laut dem Autor von nicht lsofmöglich, dies herauszufinden: Der Linux-Kernel macht diese Informationen nicht verfügbar. Quelle: Thread 2003 auf comp.unix.admin .

Die in angezeigte Nummer /proc/$pid/fd/$fdist die Inode-Nummer des Sockets im virtuellen Socket-Dateisystem. Wenn Sie ein Rohr- oder Muffenpaar erstellen, erhält jedes Ende nacheinander eine Inode-Nummer. Die Nummern werden der Reihe nach zugewiesen, daher besteht eine hohe Wahrscheinlichkeit, dass sich die Nummern um 1 unterscheiden. Dies ist jedoch nicht garantiert (entweder weil der erste Socket N war und N + 1 bereits verwendet wurde, oder weil ein anderer Thread verwendet wurde) zwischen den beiden Inode-Zuordnungen eingeplant und dieser Thread hat auch einige Inodes erzeugt).

Ich habe die Definition socketpairin Kernel 2.6.39 überprüft und die beiden Enden des Sockets sind nur durch die typspezifische socketpairMethode korreliert . Für Unix-Sockets ist das unix_socketpairinnet/unix/af_unix.c .

Gilles 'SO - hör auf böse zu sein'
quelle
2
Vielen Dank @ Gillles. Ich erinnere mich, dass ich vor einiger Zeit etwas darüber gelesen habe, es aber nicht wiedergefunden habe. Möglicherweise muss ich nur einen Patch für / proc / net / unix schreiben.
Jonathon Reinhart
Und ja, ich hatte diese Beobachtung mit den zunehmenden Inode-Zahlen gemacht, und derzeit arbeite ich damit. Wie Sie bereits bemerkt haben, kann dies jedoch nicht garantiert werden. Der Prozess, den ich betrachte, hat mindestens 40 offene Unix-Sockets, und ich sah eine Instanz, in der N + 1 nicht zutraf. Schade.
Jonathon Reinhart
1
@ JonathonReinhart Ich habe die Definition vonsocketpair überprüft , und die beiden Enden des Sockels sind nur durch die typspezifische socketpairMethode korreliert . Für Unix-Sockets ist das unix_socketpairin net / unix / af_unix.c . Es wäre schön, diese Informationen auch für Pfeifen zu haben.
Gilles 'SO- hör auf böse zu sein'
36

Hinweis : Ich verwalte jetzt einen lsofWrapper, 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_DIAGFunktion 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 Xorgan 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 lsofzu alt ist, gibt es einige weitere Optionen.

Das ssDienstprogramm (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 -pOption gibt an, bei welchen Prozessen dieser Socket geöffnet ist. Dies geschieht durch einige readlinks 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 lsofdie 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/unixTextdatei. 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_restrictParameter sysctl ausgeblendet ), wie bereits von @Totor erläutert, enthält die Kernel-Adresse einer unix_sockStruktur, die ein peerFeld enthält , das auf den entsprechenden Peer verweist unix_sock. Dies ist auch die lsofAusgabe für die DEVICESpalte in einem Unix-Socket.

Wenn Sie nun den Wert dieses peerFelds ermitteln, können Sie den Kernel-Speicher lesen und den Versatz dieses peerFelds in Bezug auf die unix_sockAdresse ermitteln.

Es wurden bereits mehrere gdb-basierte und systemtap-basierte Lösungen angegeben, für die jedoch gdb/ systemtapund 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);
Stéphane Chazelas
quelle
1
@mikeserv, das ist eine Fortsetzung dieses Kommentars . Das andere Ende von Unix-Sockets nicht finden zu können, hat mich immer geärgert (oft beim Versuch, X-Clients zu finden, und es gab kürzlich eine Frage dazu ). Ich werde versuchen zu sehen, ob ein ähnlicher Ansatz für Pseudo-Terminals verwendet werden kann, und diese dem lsofAutor vorschlagen .
Stéphane Chazelas
1
Ich kann immer noch nicht glauben, dass dies nicht vom Kernel selbst bereitgestellt wird! Ich sollte wirklich einen Patch einreichen, wenn ich nur herausfinden will, warum er noch nicht existiert.
Jonathon Reinhart
1
macht das ssnicht? Es geht mir irgendwie über den Kopf, ss -pxlistet aber viele Unix-Sockets mit Peer-Informationen wie: users: ("nacl_helper",pid=18992,fd=6),("chrome",pid=18987,fd=6),("chrome",pid=18975,fd=5)) u_str ESTAB\t0\t0\t/run/dbus/system_bus_socket 8760\t\t* 15068und die Spaltenüberschriften sind ...State\tRecv-Q\tSend-Q\tLocal Address:Port\tPeer Address:Port
mikeserv
1
Auch wenn ich es tue, lsof -c terminologykann ich sehen, terminolo 12731\tmikeserv\t12u\tunix\t0xffff880600e82680\t0t0\t1312426\ttype=STREAMaber wenn ich es tue, ss -px | grep terminologybekomme ich:u_str\tESTAB\t0\t0\t* 1312426\t*1315046\tusers:(("terminology",pid=12731,fd=12))
mikeserv
1
@mikeserv, es sieht tatsächlich so aus! Offenbar habe ich in letzter Zeit viel Zeit verschwendet ...
Stéphane Chazelas
9

Erkki Seppala hat tatsächlich ein Tool , das diese Informationen mit gdb aus dem Linux-Kernel abruft. Es ist hier verfügbar .

Caleb
quelle
2
Sehr nützliche Informationen! Auch wenn das Tool für mich nicht sofort funktioniert hat (es hat einen Kernel verursacht), hat mir die Idee geholfen, das andere Ende zu identifizieren. Ich habe meine Lösung für Stack Overflow beschrieben.
MvG,
8

Seit dem Kernel 3.3

Sie können diese Informationen jetzt abrufen mit ss:

# ss -xp

Jetzt können Sie in der PeerSpalte eine ID (Inode-Nummer) sehen, die einer anderen ID in der LocalSpalte entspricht. Passende IDs sind die beiden Enden eines Sockets.

Hinweis: Die UNIX_DIAGOption muss in Ihrem Kernel aktiviert sein.

Vor dem Kernel 3.3

Linux hat diese Informationen nicht dem Userland zugänglich gemacht.

Indem jedoch in den Kernelspeicher suchen , können wir auf diese Informationen zugreifen.

Hinweis: Diese Antwort ist so unter Verwendung gdbjedoch finden Sie @ StéphaneChazelas' Antwort , die mehr in dieser Hinsicht ausgearbeitet wird.

# lsof | grep whatever
mysqld 14450 (...) unix 0xffff8801011e8280 (...) /var/run/mysqld/mysqld.sock
mysqld 14450 (...) unix 0xffff8801011e9600 (...) /var/run/mysqld/mysqld.sock

Es gibt 2 verschiedene Steckdosen, 1 hörend und 1 hergestellt. Die Hexa-Zahl ist die Adresse der entsprechenden Kernel- unix_sockStruktur , wobei ein peerAttribut die Adresse des anderen Endes des Sockets ist (auch eine unix_sockStrukturinstanz).

Jetzt können wir gdbden peerinternen Kernel-Speicher finden:

# gdb /usr/lib/debug/boot/vmlinux-3.2.0-4-amd64 /proc/kcore
(gdb) print ((struct unix_sock*)0xffff8801011e9600)->peer
$1 = (struct sock *) 0xffff880171f078c0

# lsof | grep 0xffff880171f078c0
mysql 14815 (...) unix 0xffff880171f078c0 (...) socket

Das andere Ende der Buchse wird von mysqlPID 14815 gehalten.

Ihr Kernel muss KCORE_ELFzur Verwendung mit kompiliert werden /proc/kcore. Außerdem benötigen Sie eine Version Ihres Kernel-Images mit Debugging-Symbolen. Unter Debian 7 apt-get install linux-image-3.2.0-4-amd64-dbgwird diese Datei bereitgestellt.

Keine Notwendigkeit für das debuggbare Kernel-Image ...

Wenn Sie das Debug-Kernel-Image auf dem System nicht haben (oder nicht behalten möchten), können Sie gdbden Speicher-Offset so einstellen, dass "manuell" auf den peerWert zugegriffen wird. Dieser Offsetwert unterscheidet sich normalerweise mit der Kernelversion oder -architektur.

Auf meinem Kernel weiß ich, dass der Versatz 680 Bytes beträgt, also 85 mal 64 Bits. Also kann ich tun:

# gdb /boot/vmlinux-3.2.0-4-amd64 /proc/kcore
(gdb) print ((void**)0xffff8801011e9600)[85]
$1 = (void *) 0xffff880171f078c0

Voilà, gleiches Ergebnis wie oben.

Wenn Sie den gleichen Kernel auf mehreren Computern ausführen, ist es einfacher, diese Variante zu verwenden, da Sie nicht das Debug-Image, sondern nur den Offset-Wert benötigen.

Um diesen Offsetwert (einfach) zu ermitteln, benötigen Sie das Debug-Image:

$ pahole -C unix_sock /usr/lib/debug/boot/vmlinux-3.2.0-4-amd64
struct unix_sock {
  (...)
  struct sock *              peer;                 /*   680     8 */
  (...)
}

Hier, 680 Bytes, das sind 85 x 64 Bits oder 170 x 32 Bits.

Der größte Teil des Verdienstes für diese Antwort geht an MvG .

Totor
quelle
2
Ein anderer Ansatz zum Abrufen des Offsets könnte darin bestehen, ein Socketpaar zu erstellen, die entsprechenden Einträge in / proc / net / unix anhand der Inode-Nummern aus den Readlinks in / proc / pif / fd / * zu identifizieren und den Speicher nach der Adresse eines Sockets abzusuchen die Adresse des anderen. Dies könnte zu einer einigermaßen portablen Lösung (für Linux-Versionen und -Architekturen) führen, die von lsof selbst implementiert werden könnte. Ich werde versuchen, einen PoC zu entwickeln.
Stéphane Chazelas
2
Ich habe jetzt einen solchen PoC hinzugefügt, der auf den von mir getesteten Systemen anscheinend gut funktioniert.
Stéphane Chazelas
5

Diese Lösung, obwohl arbeiten, ist nur von begrenztem Interesse , da , wenn Sie eine aktuelle-genug systemtap haben, stehen die Chancen, werden Sie eine neu-genug - Kernel haben , wo Sie verwenden können , ssbasierend Ansätze , und wenn Sie auf einem älteren Kernel sind, dass andere Lösung , obwohl mehr Hacky eher funktioniert und keine zusätzliche Software erfordert.

Immer noch nützlich als Demonstration der Verwendung systemtapfür diese Art von Aufgabe.

Wenn Sie ein aktuelles Linux-System mit einem funktionierenden Systemtap (1.8 oder neuer) verwenden, können Sie das folgende Skript verwenden, um die Ausgabe von lsof:

Zum Beispiel:

$ lsof -aUc nm-Applet | Sudo das-Skript
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]

(Wenn Sie oben 0x0000000000000000 anstelle von 0xffff sehen ... ist dies darauf zurückzuführen, dass der kernel.kptr_restrictParameter sysctl auf Ihrem System festgelegt ist, wodurch Kernel-Zeiger vor nicht privilegierten Prozessen verborgen werden. In diesem Fall müssen Sie lsofals root ausgeführt werden, um a zu erhalten aussagekräftiges Ergebnis).

Dieses Skript unternimmt keinen Versuch, mit Socket-Dateinamen mit Zeilenumbrüchen umzugehen, aber auch nicht lsof(und auch nicht lsofmit Leerzeichen oder Doppelpunkten).

systemtapHier werden die Adresse und die Peer-Adresse aller unix_sockStrukturen im unix_socket_tableHash im Kernel gesichert.

Nur unter Linux 3.16 amd64 mit Systemtap 2.6 und 3.13 mit 2.3 getestet.

#! /usr/bin/perl
# meant to process lsof output to try and find the peer of a given
# unix domain socket. Needs a working systemtap, lsof, and superuser
# privileges. Copyright Stephane Chazelas 2015, public domain.
# Example: lsof -aUc X | sudo this-script
open STAP, '-|', 'stap', '-e', q{
  probe begin {
    offset = &@cast(0, "struct sock")->__sk_common->skc_node;
    for (i = 0; i < 512; i++) 
      for (p = @var("unix_socket_table@net/unix/af_unix.c")[i]->first;
           p;
           p=@cast(p, "struct hlist_node")->next
          ) {
        sock = p - offset;
        printf("%p %p\n", sock, @cast(sock, "struct unix_sock")->peer);
    }
    exit()
  }
};  
my %peer;
while (<STAP>) {
  chomp;
  my ($a, $b) = split;
  $peer{$a} = $b;
}
close STAP;

my %f, %addr;
open LSOF, '-|', 'lsof', '-nPUFpcfdn';
while (<LSOF>) {
  if (/(.)(.*)/) {
    $f{$1} = $2;
    if ($1 eq 'n') {
      $addr{$f{d}}->{"$f{c},$f{p}" . ($f{n} =~ m{^([@/].*?)( type=\w+)?$} ? ",$1" : "")} = "";
    }
  }
}
close LSOF;

while (<>) {
  chomp;
  for my $addr (/0x[0-9a-f]+/g) {
    my $peer = $peer{$addr};
    if (defined($peer)) {
      $_ .= $peer eq '0x0' ?
            "[LISTENING]" :
            " -> $peer\[" . join("|", keys%{$addr{$peer}}) . "]";
      last;
    }
  }
  print "$_\n";
}
Stéphane Chazelas
quelle
parse error: unknown statistic operator @var: vermisse ich etwas?
Totor,
@Totor, @varwurde am 17.06.2012 (letzter Stand ist 2.7) in systemtap 1.8 eingetragen
Stéphane Chazelas
2

4.89 von lsof unterstützt die Anzeige von Endpunktoptionen.

Zitiert aus lsof.8:

+|-E +E specifies that process intercommunication channels should be
     displayed with endpoint information and the channels
     of the endpoints should also be displayed.  Currently
     only pipe on Linux is implemented.

     Endpoint information is displayed in the NAME column
     in the form "PID,cmd,FDmode".  PID is the endpoint
     process ID; cmd is the endpoint process command; FD is
     the endpoint file's descriptor; and mode is the
     endpoint file's access mode.  Multiple occurrences of
     this information can appear in a file's NAME column.

     -E specfies that Linux pipe files should only be
     displayed with endpoint information.

Ausgabebeispiel:

mozStorag 21535 22254  yamato    6u     unix 0xf...       0t0     348924 type=STREAM pino=351122 4249,dbus-daem,55u
mozStorag 21535 22254  yamato   10u     unix 0xf...       0t0     356193 type=STREAM pino=356194 21535,gdbus,11u
mozStorag 21535 22254  yamato   11u     unix 0xf...       0t0     356194 type=STREAM pino=356193 21535,gdbus,10u
mozStorag 21535 22254  yamato   21u     unix 0xf...       0t0     355141 type=STREAM pino=357544 4249,dbus-daem,60u
mozStorag 21535 22254  yamato   26u     unix 0xf...       0t0     351134 type=STREAM pino=355142 5015,gdbus,17u
mozStorag 21535 22254  yamato   69u     unix 0xf...       0t0     469354 type=STREAM pino=468160 4545,alsa-sink,21u
mozStorag 21535 22254  yamato   82u     unix 0xf...       0t0     449383 type=STREAM pino=449384 12257,Chrome_Ch,3u
mozStorag 21535 22254  yamato   86u     unix 0xf...       0t0     355174 type=SEQPACKET pino=355175 21535,gdbus,95u
mozStorag 21535 22254  yamato   95u     unix 0xf...       0t0     355175 type=SEQPACKET pino=355174 21535,gdbus,86u 12257,Chrome_Ch,4u
mozStorag 21535 22254  yamato  100u     unix 0xf...       0t0     449389 type=STREAM pino=456453 3614,Xorg,38u
mozStorag 21535 22254  yamato  105u     unix 0xf...       0t0     582613 type=STREAM pino=586261
obexd     22163        yamato    1u     unix 0xf...       0t0     361859 type=STREAM pino=365931
obexd     22163        yamato    2u     unix 0xf...       0t0     361860 type=STREAM pino=365934
obexd     22163        yamato    3u     unix 0xf...       0t0     361241 type=DGRAM pino=10028
obexd     22163        yamato    6u     unix 0xf...       0t0     361242 type=STREAM pino=361864 4249,dbus-daem,70u
Masatake YAMATO
quelle
2

Seit Linux-Kernel 4.2 gibt es dort CONFIG_UNIX_DIAGzusätzliche Informationen zu UNIX-Domain-Sockets, nämlich die Virtual File System(VFS-) Informationen, die die bisher fehlenden Informationen enthalten, um den Inode vom Pfad zum Prozess zu verlinken. Es kann bereits mit dem ssTool von iproute2 ab Version 4.19.0 ~ 55 abgefragt werden :

$ ss --processes --unix --all --extened
...
Netid  State   Recv-Q  Send-Q  Local Address:Port      Peer Address:Port
u_str  LISTEN  0       5         /tmp/socket 13381347             * 0     users:(("nc",pid=12550,fd=3)) <-> ino:1569897 dev:0/65025 peers:

Die Gerätenummer und der Pfad-Inode, die Sie erhalten können

$ stat -c 'ino:%i dev:0/%d' /tmp/socket
ino:1569946 dev:0/65025

ss unterstützt auch die Filterung:

 ss --processes --unix --all --extended 'sport = /tmp/socket'

Beachten Sie jedoch, dass dies möglicherweise nicht den richtigen Socket für Sie auflistet, da ein böser Prozess Ihren ursprünglichen Socket möglicherweise umbenennen und durch einen eigenen bösen Socket ersetzen kann:

mv /tmp/socket /tmp/socket.orig
nc -U -l /tmp/socket.evil &
mv /tmp/socket.evil /tmp/socket

lsof /tmp/socket, fuser /tmp/socketUnd ss --processes --unix --all --extended 'sport = /tmp/socket'wird alle ursprünglichen Prozesslisten, nicht den Bösen Ersatzes. Verwenden Sie stattdessen Folgendes:

id=$(stat -c 'ino:%i dev:0/%d' /tmp/socket)
ss --processes --unix --all --extended | grep -F "$id"

Oder schreiben Sie Ihr eigenes kleines Programm basierend auf der Vorlage in man 7 sock_diag .

pmhahn
quelle