Comment capturer passivement à partir des sockets de domaine Unix (surveillance des sockets AF_UNIX)?

13

Les captures TCP / IP et UDP peuvent être effectuées à l'aide de tcpdump/ dumpcapet produisent un fichier pcap / pcapng qui peut être envoyé à Wireshark pour une analyse plus approfondie. Existe-t-il un outil similaire pour les sockets de domaine Unix nommés? (Une solution générale qui fonctionne pour les sockets abstraites serait bien aussi.)

stracetel quel n'est pas suffisant, il n'est pas simple de filtrer les E / S des sockets de domaine Unix. Un proxy utilisant socatou similaire ne convient pas non plus car l'objectif est une analyse passive pour les programmes ouverts existants.

Comment puis-je obtenir une capture de paquets que je peux utiliser dans Wireshark pour l'analyse? Les exemples d'application de protocole sont X11 (Xorg, mon application actuelle) et cURL / PHP (HTTP). J'ai vu une CONFIG_UNIX_DIAGoption dans le noyau Linux, est-ce utile?

Lekensteyn
la source
1
Voir Capturer le trafic du protocole X11
Stéphane Chazelas
@ StéphaneChazelas Merci, mais depuis le démarrage de Xorg -nolisten tcp, il n'y a pas de socket TCP. Si tout échoue, je reviendrai probablement sur l'utilisation de xscope ou de votre astuce strace + text2pcap. Je serais toujours intéressé par une capture de socket Unix générique (pour les données uniquement, pas les données de canal latéral).
Lekensteyn
En plus de strace, vous pouvez également consulter auditd et systemtap.
Stéphane Chazelas
systemtap ressemble presque à un hack GDB, mais au niveau du noyau. Je ne connais pas l'audit, je n'ai trouvé qu'un hook LSM qui vérifiait si vous étiez autorisé à lire / écrire. (Je suis en train de creuser dans le code source du noyau Linux)
Lekensteyn

Réponses:

12

À partir du noyau Linux v4.2-rc5, il n'est pas possible de capturer directement en utilisant les interfaces utilisées par libpcap. libpcap utilise le domaine spécifique à Linux AF_PACKET(alias PF_PACKET) qui vous permet uniquement de capturer des données pour des données passant par un " netdevice " (comme les interfaces Ethernet).

Il n'y a pas d'interface noyau pour la capture à partir de AF_UNIXsockets. Les captures Ethernet standard ont un en-tête Ethernet avec la source / destination, etc. Les sockets Unix n'ont pas un tel faux en-tête et le registre des types d'en-tête de couche liaison ne répertorie aucun élément lié à cela.

Les points d'entrée de base pour les données sont unix_stream_recvmsget unix_stream_sendmsgpour SOCK_STREAM( SOCK_DGRAMet SOCK_SEQPACKETont des fonctions nommées de manière similaire). Les données sont mises en mémoire tampon dans sk->sk_receive_queueet dans la unix_stream_sendmsgfonction , il n'y a pas de code qui conduit finalement à appeler la tpacket_rcvfonction pour les captures de paquets. Voir cette analyse par osgx sur SO pour plus de détails sur les internes de la capture de paquets en général.

Revenons à la question d'origine sur la AF_UNIXsurveillance des sockets, si vous êtes principalement intéressé par les données d'application, vous avez quelques options:

  • Passif (fonctionne également pour les processus déjà en cours d'exécution):
    • Utilisez straceet capturez les appels système possibles qui effectuent des E / S. Il y a beaucoup d'entre eux, read, pread64, readv, preadv, recvmsget beaucoup d' autres ... Voir @ Stéphane Chazelas exemple xterm. L'inconvénient de cette approche est que vous devez d'abord trouver votre descripteur de fichier, puis manquer des appels système. Avec strace, vous pouvez utiliser la -e trace=fileplupart d'entre eux ( preadn'est couvert que par -e trace=desc, mais il n'est probablement pas utilisé pour les sockets Unix par la plupart des programmes).
    • Activez / modifiez unix_stream_recvmsg, unix_stream_sendmsg(ou unix_dgram_*ou unix_seqpacket_*) dans le noyau et affichez les données quelque part. Vous pouvez utiliser SystemTap pour définir de tels points de trace, voici un exemple pour surveiller les messages sortants. Nécessite la prise en charge du noyau et la disponibilité des symboles de débogage .
  • Actif (ne fonctionne que pour les nouveaux processus):

    • Utilisez un proxy qui écrit également des fichiers. Vous pouvez écrire un multiplexeur rapide vous-même ou pirater quelque chose comme ça qui génère également un pcap (méfiez-vous des limitations, par exemple, vous AF_UNIXpouvez passer des descripteurs de fichiers, AF_INETne pouvez pas):

      # fake TCP server connects to real Unix socket
      socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CONNECT:some.sock
      # start packet capture on said port
      tcpdump -i lo -f 'tcp port 6000'
      # clients connect to this Unix socket
      socat UNIX-LISTEN:fake.sock,fork TCP-CONNECT:127.0.0.1:6000
      
    • Utilisez un proxy d'application dédié. Pour X11, il y a xscope ( git , manuel ).

L' CONFIG_UNIX_DIAGoption suggérée n'est malheureusement pas utile ici non plus, elle ne peut être utilisée que pour collecter des statistiques, pas pour acquérir des données en temps réel au fur et à mesure (voir linux / unix_diag.h ).

Malheureusement, il n'existe actuellement aucun traceur parfait pour les sockets de domaine Unix qui produisent des pcaps (à ma connaissance). Idéalement, il y aurait un format libpcap qui a un en-tête contenant le PID source / dest (lorsqu'il est disponible) suivi de données supplémentaires facultatives (informations d'identification, descripteurs de fichier) et enfin les données. Faute de quoi, le mieux qui puisse être fait est le traçage des appels système.


Informations supplémentaires (pour le lecteur intéressé), voici quelques backtraces (acquises avec GDB se cassant sur unix_stream_*et rbreak packet.c:., Linux dans QEMU et socat sur Linux 4.2-rc5 principal):

# echo foo | socat - UNIX-LISTEN:/foo &
# echo bar | socat - UNIX-CONNECT:/foo
unix_stream_sendmsg at net/unix/af_unix.c:1638
sock_sendmsg_nosec at net/socket.c:610
sock_sendmsg at net/socket.c:620
sock_write_iter at net/socket.c:819
new_sync_write at fs/read_write.c:478
__vfs_write at fs/read_write.c:491
vfs_write at fs/read_write.c:538
SYSC_write at fs/read_write.c:585
SyS_write at fs/read_write.c:577
entry_SYSCALL_64_fastpath at arch/x86/entry/entry_64.S:186

unix_stream_recvmsg at net/unix/af_unix.c:2210
sock_recvmsg_nosec at net/socket.c:712
sock_recvmsg at net/socket.c:720
sock_read_iter at net/socket.c:797
new_sync_read at fs/read_write.c:422
__vfs_read at fs/read_write.c:434
vfs_read at fs/read_write.c:454
SYSC_read at fs/read_write.c:569
SyS_read at fs/read_write.c:562

# tcpdump -i lo &
# echo foo | socat - TCP-LISTEN:1337 &
# echo bar | socat - TCP-CONNECT:127.0.0.1:1337
tpacket_rcv at net/packet/af_packet.c:1962
dev_queue_xmit_nit at net/core/dev.c:1862
xmit_one at net/core/dev.c:2679
dev_hard_start_xmit at net/core/dev.c:2699
__dev_queue_xmit at net/core/dev.c:3104
dev_queue_xmit_sk at net/core/dev.c:3138
dev_queue_xmit at netdevice.h:2190
neigh_hh_output at include/net/neighbour.h:467
dst_neigh_output at include/net/dst.h:401
ip_finish_output2 at net/ipv4/ip_output.c:210
ip_finish_output at net/ipv4/ip_output.c:284
ip_output at net/ipv4/ip_output.c:356
dst_output_sk at include/net/dst.h:440
ip_local_out_sk at net/ipv4/ip_output.c:119
ip_local_out at include/net/ip.h:119
ip_queue_xmit at net/ipv4/ip_output.c:454
tcp_transmit_skb at net/ipv4/tcp_output.c:1039
tcp_write_xmit at net/ipv4/tcp_output.c:2128
__tcp_push_pending_frames at net/ipv4/tcp_output.c:2303
tcp_push at net/ipv4/tcp.c:689
tcp_sendmsg at net/ipv4/tcp.c:1276
inet_sendmsg at net/ipv4/af_inet.c:733
sock_sendmsg_nosec at net/socket.c:610
sock_sendmsg at net/socket.c:620
sock_write_iter at net/socket.c:819
new_sync_write at fs/read_write.c:478
__vfs_write at fs/read_write.c:491
vfs_write at fs/read_write.c:538
SYSC_write at fs/read_write.c:585
SyS_write at fs/read_write.c:577
entry_SYSCALL_64_fastpath at arch/x86/entry/entry_64.S:186

tpacket_rcv at net/packet/af_packet.c:1962
dev_queue_xmit_nit at net/core/dev.c:1862
xmit_one at net/core/dev.c:2679
dev_hard_start_xmit at net/core/dev.c:2699
__dev_queue_xmit at net/core/dev.c:3104
dev_queue_xmit_sk at net/core/dev.c:3138
dev_queue_xmit at netdevice.h:2190
neigh_hh_output at include/net/neighbour.h:467
dst_neigh_output at include/net/dst.h:401
ip_finish_output2 at net/ipv4/ip_output.c:210
ip_finish_output at net/ipv4/ip_output.c:284
ip_output at net/ipv4/ip_output.c:356
dst_output_sk at include/net/dst.h:440
ip_local_out_sk at net/ipv4/ip_output.c:119
ip_local_out at include/net/ip.h:119
ip_queue_xmit at net/ipv4/ip_output.c:454
tcp_transmit_skb at net/ipv4/tcp_output.c:1039
tcp_send_ack at net/ipv4/tcp_output.c:3375
__tcp_ack_snd_check at net/ipv4/tcp_input.c:4901
tcp_ack_snd_check at net/ipv4/tcp_input.c:4914
tcp_rcv_state_process at net/ipv4/tcp_input.c:5937
tcp_v4_do_rcv at net/ipv4/tcp_ipv4.c:1423
tcp_v4_rcv at net/ipv4/tcp_ipv4.c:1633
ip_local_deliver_finish at net/ipv4/ip_input.c:216
ip_local_deliver at net/ipv4/ip_input.c:256
dst_input at include/net/dst.h:450
ip_rcv_finish at net/ipv4/ip_input.c:367
ip_rcv at net/ipv4/ip_input.c:455
__netif_receive_skb_core at net/core/dev.c:3892
__netif_receive_skb at net/core/dev.c:3927
process_backlog at net/core/dev.c:4504
napi_poll at net/core/dev.c:4743
net_rx_action at net/core/dev.c:4808
__do_softirq at kernel/softirq.c:273
do_softirq_own_stack at arch/x86/entry/entry_64.S:970
Lekensteyn
la source
Soit dit en passant, si vous avez lu kristrev.github.io/2013/07/26/… et vu des instructions pour surveiller les notifications de liaison via netlink et que vous vous demandez si les diagnostics peuvent fournir un reniflement de paquets, la réponse est toujours non . Ces diagnostics fournissent des statistiques par interrogation et non en temps réel.
Lekensteyn
9

J'ai écrit un outil pour capturer et vider le trafic de socket de domaine Unix. Il utilise bpf/kprobepour sonder la fonction du noyau unix_stream_sendmsget vider le trafic vers l'espace utilisateur.

L'outil dépend bcc, vous devez donc d' bccabord l' installer .

Un exemple de course:

$ sudo ./sockdump.py /var/run/docker.sock # run "docker ps" in another terminal
>>> docker[3412] len 83
GET /_ping HTTP/1.1
Host: docker
User-Agent: Docker-Client/18.06.1-ce (linux)

>>> dockerd[370] len 215
HTTP/1.1 200 OK
Api-Version: 1.38
Docker-Experimental: false
Ostype: linux
Server: Docker/18.06.1-ce (linux)
Date: Tue, 25 Sep 2018 07:05:03 GMT
Content-Length: 2
Content-Type: text/plain; charset=utf-8

OK
...
mechpen
la source