orm@doc-tcpip.org | Erstellt: April 2001 - Letzte Modifikation: Oktober 2003 |
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.
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 END0000000000003500 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.
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.
[ Allgemein | UNIX | AIX | TCP-IP | TCP | ROUTING | DNS | NTP | NFS | FreeBSD | Linux | RPi | SMTP | Tracing | GPS | LW ]
Copyright 2001-2021 by Orm Hager - Es gilt die GPL