¿Cómo capturar pasivamente desde sockets de dominio Unix (monitoreo de socket AF_UNIX)?

13

Las capturas TCP / IP y UDP se pueden hacer usando tcpdump/ dumpcapy produce un archivo pcap / pcapng que se puede alimentar a Wireshark para su posterior análisis. ¿Existe una herramienta similar para los sockets de dominio Unix con nombre? (Sin embargo, una solución general que funcione para sockets abstractos también sería buena).

straceas-is no es suficiente, no es sencillo filtrar por E / S de sockets de dominio Unix. Un proxy que usa socato no tampoco es adecuado ya que el objetivo es el análisis pasivo de los programas abiertos existentes.

¿Cómo puedo obtener una captura de paquetes que pueda usar en Wireshark para el análisis? Las aplicaciones de protocolo de ejemplo son X11 (Xorg, mi aplicación actual) y cURL / PHP (HTTP). He visto una CONFIG_UNIX_DIAGopción en el kernel de Linux, ¿es útil?

Lekensteyn
fuente
1
Vea Capturar el tráfico del protocolo X11
Stéphane Chazelas
@ StéphaneChazelas Gracias, pero desde que comenzó Xorg -nolisten tcp, no hay un socket TCP. Si todo falla, probablemente volveré a usar xscope o tu truco ordenado strace + text2pcap. Sin embargo, todavía estaría interesado en una captura genérica de socket Unix (solo para datos, no datos de canal lateral).
Lekensteyn
Además de strace, también puede mirar auditado y systemtap.
Stéphane Chazelas
systemtap casi parece un truco GDB, pero luego a nivel de kernel. No sé sobre auditoría, solo encontré un enlace LSM que verificó si se le permite leer / escribir. (Actualmente estoy cavando en el código fuente del kernel de Linux)
Lekensteyn

Respuestas:

12

A partir del kernel de Linux v4.2-rc5, no es posible capturar directamente utilizando las interfaces que libpcap está utilizando. libpcap utiliza el dominio específico de Linux AF_PACKET(alias PF_PACKET) que solo le permite capturar datos para los datos que pasan por un " dispositivo de red " (como las interfaces Ethernet).

No hay interfaz de kernel para capturar desde AF_UNIXsockets. Las capturas estándar de Ethernet tienen un encabezado Ethernet con origen / destino, etc. Los sockets Unix no tienen dicho encabezado falso y el registro de tipos de encabezado de capa de enlace no enumera nada relacionado con esto.

Los puntos de entrada básicos para los datos son unix_stream_recvmsgy unix_stream_sendmsgpara SOCK_STREAM( SOCK_DGRAMy SOCK_SEQPACKETtienen funciones con nombres similares). Los datos están almacenados en sk->sk_receive_queuela unix_stream_sendmsgfunción , y no hay ningún código que finalmente lleve a llamar a la tpacket_rcvfunción para la captura de paquetes. Consulte este análisis de osgx en SO para obtener más detalles sobre los aspectos internos de la captura de paquetes en general.

Volviendo a la pregunta original sobre el AF_UNIXmonitoreo de sockets, si está interesado principalmente en los datos de la aplicación, tiene algunas opciones:

  • Pasivo (también funciona para procesos que ya se están ejecutando):
    • Use stracey capture las posibles llamadas al sistema que realizan E / S. Hay un montón de ellos, read, pread64, readv, preadv, recvmsgy muchos más ... Ver @ Stéphane Chazelas ejemplo para xterm. La desventaja de este enfoque es que primero tiene que encontrar su descriptor de archivo y luego aún puede perder llamadas del sistema. Con strace puedes usarlo -e trace=filepara la mayoría de ellos ( preadsolo está cubierto por -e trace=desc, pero probablemente no se usa para sockets Unix en la mayoría de los programas).
    • Romper / modificar unix_stream_recvmsg, unix_stream_sendmsg(o unix_dgram_*o unix_seqpacket_*) en el núcleo y generar los datos, en algún lugar. Puede usar SystemTap para configurar dichos puntos de rastreo, aquí hay un ejemplo para monitorear los mensajes salientes. Requiere soporte de kernel y disponibilidad de símbolos de depuración .
  • Activo (solo funciona para nuevos procesos):

    • Use un proxy que también escriba archivos. Puede escribir un multiplexor rápido usted mismo o piratear algo como esto que también genera un pcap (tenga cuidado con las limitaciones, por ejemplo, AF_UNIXpuede pasar descriptores de archivo, AF_INETno puede):

      # 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
      
    • Use un proxy de aplicación dedicado. Para X11, hay xscope ( git , manual ).

CONFIG_UNIX_DIAGDesafortunadamente, la opción sugerida tampoco es útil aquí, solo se puede usar para recopilar estadísticas, no para adquirir datos en tiempo real a medida que fluyen (consulte linux / unix_diag.h ).

Desafortunadamente, no hay trazadores perfectos en este momento para los sockets de dominio Unix que producen pcaps (que yo sepa). Lo ideal sería un formato libpcap que tenga un encabezado que contenga el PID de origen / destino (cuando esté disponible) seguido de datos adicionales opcionales (credenciales, descriptores de archivo) y finalmente los datos. A falta de eso, lo mejor que se puede hacer es el seguimiento de syscall.


Información adicional (para el lector interesado), aquí hay algunos retrocesos (adquiridos con GDB rompiendo unix_stream_*y rbreak packet.c:.Linux en QEMU y socat en Linux 4.2-rc5 mainline):

# 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
fuente
Por cierto, si ha leído kristrev.github.io/2013/07/26/… y vio instrucciones para observar las notificaciones de enlace a través de netlink y se preguntó si los diagnósticos pueden proporcionar la detección de paquetes, la respuesta sigue siendo negativa . Estos diagnósticos proporcionan estadísticas por sondeo, no en tiempo real.
Lekensteyn
9

Escribí una herramienta para capturar y volcar el tráfico de socket de dominio Unix. Se utiliza bpf/kprobepara sondear la función del núcleo unix_stream_sendmsgy volcar el tráfico en el espacio del usuario.

La herramienta depende de bcc, por lo que debe instalar bccprimero.

Un ejemplo de ejecución:

$ 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
fuente