orm@doc-tcpip.org

Erstellt: April 2001 - Letzte Modifikation: Oktober 2003

[ Main | Local ]


Welcher Prozess hält diesen Port?

Spurensuche per netstat, crash und kdb.

Das Problem

Ich möchte einen Dienst starten, der sich an einen gewissen Port bindet. Der Daemon fällt aber immer hin, im Log finde ich etwas in dieser Art: Socket already in use.

Also hat schon ein anderer Daemon/Prozess diesen Socket blockiert. Ob das so ist, kann ich mit dem netstat-Kommando sehen: netstat -aAnf inet zeigt alle Internet-Protokoll Ports; die im Status LISTEN stehen, sind an einen Prozess gebunden und somit blockiert (das hat übrigens gar nichts mit der völlig unverbindlichen /etc/services zu tun).

Man steht jetzt vor der Aufgabe, von der Netstat-Ausgabe auf den Prozess zu schliessen, um diesen zu stoppen.

Vorgehen unter AIX 5 - und etwas Theorie

Unter AIX 5.x ist der Weg vom Port zur PID sehr einfach, der Kerneldebugger kdb macht eigentlich alles. Das Netstat-Kommando teilt die Adresse des Protocol Control Blocks (PCB) mit, eine Hex-Zahl. Anhand dieser Zahl und der Tatsache, das es sich um einen TCP-Socket handelt, stellt das kdb-Unterkommando sockinfo alle wichtigen Daten zur Verfügung.

Diese Information, also die verschiedenen Kontroll-Blöcke, stehen in Datenstrukturen, die in Form einer Linked List verknüpft sind. Dabei wird aus einem Kontrollblock meist mehrmals auf andere Kontrollblöcke verwiesen.

Der kdb geht diesen Verweisen nach und stellt die Information lesbar zusammen - der Protocol Control Block, im Beispiel für TCP (daher TCPCB), verweist auf den InterNet Protocol Control Block (INPCB), der dann wieder auf den Socket verweist.

Hier ein Beispiel:

  
root@oepd1:/tmp> netstat -aAnf inet
Active Internet connections (including servers)
PCB/ADDR Proto Recv-Q Send-Q  Local Address      Foreign Address    (state)
700495e4 tcp        0      0  *.21               *.*                LISTEN
700491e4 tcp        0      0  *.23               *.*                LISTEN
70094de4 tcp4       0      0  *.25               *.*                LISTEN
70049de4 tcp4       0      0  *.111              *.*                LISTEN
7003b5e4 tcp4       0      0  *.199              *.*                LISTEN
700945e4 tcp4       0      0  *.1334             *.*                LISTEN
^^^^^^^^                        ^^^^ 
7009d9e4 tcp4       0      0  10.55.17.115.32783 10.55.17.115.601   ESTABLISHED

Ich möchte nun wissen, was es mit dem Port 1334 auf sich hat und merke mit daher die Adresse/Nummer des Protocol Control Block, das ist die Spalte PCB/ADDR: 700945e4.
Dann starte ich den kdb:

 
root@oepd1:/tmp> kdb
The specified kernel file is a UP kernel
Preserving 941425 bytes of symbol table
First symbol __mulh
           START              END 
0000000000003500 000000000174BEC8 _system_configuration+000020
000000002FF3B400 000000002FF80A70 __ublock+000000
000000002FF22FF4 000000002FF22FF8 environ+000000
000000002FF22FF8 000000002FF22FFC errno+000000
00000000E0000000 00000000F0000000 lkwseg+10000000
PFT:
id....................0007
raddr.....0000000000600000 eaddr.....0000000000600000
size..............00000000 align.............00000000
valid..1 ros....0 holes..0 io.....0 seg....1 wimg...2

PVT:
id....................0008
raddr.....0000000000800000 eaddr.....0000000000000000
size..............00000000 align.............00000000
valid..1 ros....0 holes..0 io.....0 seg....1 wimg...2

Bis hierher ist es allgemeiner Blurb. Jetzt wird sockinfo-Unterkommando abgesetzt, mit der oben ermittelten Adresse als Argument sowie dem Typ des Socket (TCP - das findet sich auch in der Netstat-Asugabe). Dieses Kommando ist so nett und zieht alle verwandten Datenstrukturen mit an. Das ist einmal der InterNet Protocol Control Block (INPCB) und der Socket. Das sieht man an dem Symbol @, und referenziert wird es mit dem entsprechenden Namen (t_inpcb, socket). Die Adresse wird als @ Nummer angegeben. Mit dem crash-Kommando muß man das per Hand heraus finden.

 
(0)> sockinfo 700945e4 tcpcb
---- TCPCB ----(@ 700945E4)----
    seg_next...... 700945E4 seg_prev...... 700945E4 
    t_softerror... 00000000 t_state....... 00000001 (LISTEN)
    t_timer....... 00000000 (TCPT_REXMT)
    t_timer....... 00000000 (TCPT_PERSIST)
    t_timer....... 00000000 (TCPT_KEEP)
    t_timer....... 00000000 (TCPT_2MSL)
    t_rxtshift.... 00000000 t_rxtcur...... 00000006 t_dupacks..... 00000000 
    t_maxseg...... 00000200 t_force....... 00000000 
    t_flags....... 00000000 ()
    t_oobflags.... 00000000 ()
    t_iobc........ 00000000 t_template.... 7009460C t_inpcb....... 70094540 
##                                                  ^^^^^^^^^^^^^^^^^^^^^^^
    t_timestamp... 9146F201 snd_una....... 00000000 snd_nxt....... 00000000 
    snd_up........ 00000000 snd_wl1....... 00000000 snd_wl2....... 00000000 
    iss........... 00000000 snd_wnd....... 00000000 rcv_wnd....... 00000000 
    rcv_nxt....... 00000000 rcv_up........ 00000000 irs........... 00000000 
    snd_wnd_scale. 00000000 rcv_wnd_scale. 00000000 req_scale_sent 00000000 
    req_scale_rcvd 00000000 last_ack_sent. 00000000 timestamp_rec. 00000000 
    timestamp_age. 0000000B rcv_adv....... 00000000 snd_max....... 00000000 
    snd_cwnd...... 3FFFC000 snd_ssthresh.. 3FFFC000 t_idle........ 0000000B 
    t_rtt......... 00000000 t_rtseq....... 00000000 t_srtt........ 00000000 
    t_rttvar...... 00000006 t_rttmin...... 00000002 max_rcvd...... 00000000 
    max_sndwnd.... 00000000 t_peermaxseg.. 00000200 snd_in_pipe... 00000000 
    sack_data..... 00000000 snd_recover... 00000000 snd_high...... 00000000 
    snd_ecn_max... 00000000 snd_ecn_clear. 00000000 t_splice_with. 00000000 
    t_splice_flags 00000000 

-------- TCB --------- INPCB  INFO ----(@ 70094540)----
    next........ 00000000 prev........ 00000000 head........ 05519580 
    iflowinfo... 00000000 faddr_6... @ 70094554 fport....... 00000000 
    fatype...... 00000000 oflowinfo... 00000000 laddr_6... @ 7009456C 
    lport....... 00000536 latype...... 00000000 socket...... 70094400 
##  ^^^^^^^^^^^^^^^^^^^^^                       ^^^^^^^^^^^^^^^^^^^^^    
    ppcb........ 700945E4 route_6... @ 70094584 ifa......... 00000000 
##  ^^^^^^^^^^^^^^^^^^^^^    
    flags....... 00000400 proto....... 00000000 tos......... 00000000 
    ttl......... 0000003C rcvttl...... 00000000 rcvif....... 00000000 
    options..... 00000000 refcnt...... 00000001 
    lock........ 00000000 rc_lock..... 00000000 moptions.... 00000000 
    hash.next... 05535A38 hash.prev... 05535A38 
    timewait.nxt 00000000 timewait.prv 00000000 
---- SOCKET INFO ----(@ 70094400)----
    type........ 0001 (STREAM)
    opts........ 0002 (ACCEPTCONN)
    linger...... 0000 state....... 0080 (PRIV)
    pcb..... 70094540 proto... 05515818 lock.... 7003FC40 head.... 00000000 
##  ^^^^^^^^^^^^^^^^^
    q0...... 00000000 q....... 00000000 dq...... 00000000 q0len....... 0000 
    qlen........ 0000 qlimit...... 0005 dqlen....... 0000 timeo....... 0000 
    error....... 0000 special..... 0E08 pgid.... 00000000 oobmark. 00000000 
snd:cc...... 00000000 hiwat... 00004000 mbcnt... 00000000 mbmax... 00010000 
    lowat... 00001000 mb...... 00000000 sel..... 00000000 events...... 0000 
    iodone.. 00000000 ioargs.. 00000000 lastpkt. 00000000 wakeone. FFFFFFFF 
    timer... 00000000 timeo... 00000000 flags....... 0000 ()
    wakeup.. 00000000 wakearg. 00000000 lock.... 7003FC44 
rcv:cc...... 00000000 hiwat... 00004000 mbcnt... 00000000 mbmax... 00010000 
    lowat... 00000001 mb...... 00000000 sel..... 00000000 events...... 0000 
    iodone.. 00000000 ioargs.. 00000000 lastpkt. 00000000 wakeone. FFFFFFFF 
    timer... 00000000 timeo... 00000000 flags....... 0000 ()
    wakeup.. 00000000 wakearg. 00000000 lock.... 7003FC48 
    tpcb.... 00000000 fdev_ch. 00000000 sec_info 00000000 qos..... 00000000 
    gidlist. 00000000 private. 00000000 uid..... 00000000 bufsize. 00000000 
    threadcnt00000000 nextfree 00000000 siguid.. 00000000 sigeuid. 00000000 
    sigpriv. 00000000 
    sndtime. 00000000 sec 00000000 usec rcvtime. 00000000 sec 00000000 usec
proc/fd:  39/3
proc/fd: fd: 3
              SLOT NAME     STATE    PID  PPID  PGRP   UID  ADSPACE  CL #THS

pvproc+004E00   39*writesrv ACTIVE 02756 00CAE 02756 00000 00000681  65 0001

Das ist für Dödel: Der Name des Prozess, und sein PID, allerdings in Hex. Weiter oben (@INPCB) findet sich noch der lport, das ist der belegte Port, auch in Hex. Mit dem bc ist das leicht zu rechnen (es geht auch im kdb, aber ich habe das Kommando vergessen):

 
root@oepd1:/tmp> bc
ibase=16         - Eingabe -
2756             - Eingabe -
10070
536              - Eingabe -
1334     
quit             - Eingabe -

Jetzt kann man anhand der PID den Prozess finden und im Zweifelsfall erschlagen.

 
root@oepd1:/tmp> ps -ef|more
     UID   PID  PPID   C    STIME    TTY  TIME CMD
    root     1     0   0   Sep 03      -  2:40 /etc/init 
    root  2534     1   0   Sep 03      -  0:00 /usr/dt/bin/dtlogin -daemon 
    root  3246     1   0   Sep 03      -  0:00 /usr/sbin/srcmstr 
........
    root 10070  3246   0   Sep 03      -  0:00 /usr/sbin/writesrv 
........

Es lohnt sich, die Ausgabe des sockinfo Kommandos genau anzusehen. Man findet hier sämtliche Information über alle Strukturen, die zu einem Socket gehören. Alle Parameter, Flags etc. - und es ist aktuell. Bei der Fehlersuche wertvoll.

Mit UDP ist das ein wenig schwieriger, da muß man den richtigen Moment erwischen, es müßen Daten fließen. Im kdb muß man statt tcpcb allerdings inpcb angeben.

Vorgehen unter AIX 4

Im Fall von AIX 4 ist das alles nicht so erfreulich. Hier gibt es den kdb noch nicht; der Kernel-Debugger heißt hier crash.

Dieses Kommando ist vom Umfang her noch nicht so ausgereift und gibt daher nicht selbstständig die referenzierten Kontroll-Blöcke aus.

Das muß also hier der Administrator machen. Bedauerlicherweise gibt es einen Punkt, über den man nur durch Abarbeiten der Liste der momentan aktiven Kontroll-Blöcke kommt. Dazu muß man von den Prozessen/Threads ausgehen und im Prinzip für jeden Prozess die zugehörigen Kontroll-Blöcke abgehen, um dann festzustellen, das es nicht der gesuchte Port ist. Das ist nur bei sehr kurzen Prozesslisten machbar.

Umgekehrt, also bei einem gegeben Prozess, festzustellen, welcher Port gebunden ist, ist relativ trivial.

Hier summarisch die Befehlsfolge im crash-Kommando:

# crash /dev/mem /unix
> proc | grep 3270            (es ging hier um einen Terminalprozess)
==> man erhält die Nummer des Prozesses, hier 91.
> th -p 91
==> man erhält die Slot-Id, hier 284
> user 284 
==> zeigt das UTHREAD AREA des Prozess-Slots.
Man erhält am Ende die "File Descriptor Area".
Das sind alle offenen Files, Sockets etc.
Diese muß man alle durchgehen, wozu man die fp Adresse abfragt
(hier 0x10007290).
> file | grep 10007290
==> Man erhält den Typ (zb. "socket.STREAM") und die Adresse
des Socket (hier 700d5400).
> socket 700d5400
==> Damit findet man die Adresse des Socket Control Block, hier
700d5644).
Man startet nun den Network-Debugger im Crash:
> ndb
ndb> pcb 700d5644
==> Damit habe ich alle Informationen über den Socket, IPs, Ports,
Flags und den Protocol Control Block.
der pcb findet sich auch im netstat -aA, den man im Crash nach
beenden des ndb starten kann:
ndb> q
> netstat -aA

Das wäre auch der umgekehrte Weg, alle Sockets nach einem pcb 
durchsuchen. Das ist aber mühsam bis unmöglich, wenn viele 
Prozesse und Verbindungen aktiv sind.


[ Main | Local ]

[ Allgemein | UNIX | AIX | TCP-IP | TCP | ROUTING | DNS | NTP | NFS | FreeBSD | Linux | SMTP | Tracing | GPS ]

Copyright 2001-2014 by Orm Hager - Es gilt die GPL
Feedback bitte an: Orm Hager (orm@doc-tcpip.org )