Como capturar passivamente de soquetes de domínio Unix (monitoramento de soquete AF_UNIX)?

13

As capturas TCP / IP e UDP podem ser feitas usando tcpdump/ dumpcape produz um arquivo pcap / pcapng que pode ser alimentado no Wireshark para análise posterior. Existe uma ferramenta semelhante para soquetes de domínio Unix nomeados? (Uma solução geral que funcione para soquetes abstratos também seria interessante.)

stracecomo está não é suficiente, não é fácil filtrar E / S de soquetes de domínio Unix. Um proxy usando socatou semelhante também não é adequado, pois o objetivo é a análise passiva para programas abertos existentes.

Como posso obter uma captura de pacote que posso usar no Wireshark para análise? Exemplos de aplicativos de protocolo são X11 (Xorg, meu aplicativo atual) e cURL / PHP (HTTP). Eu vi uma CONFIG_UNIX_DIAGopção no kernel do Linux, isso é útil?

Lekensteyn
fonte
1
Veja Capturar o tráfego do protocolo X11
Stéphane Chazelas
@ StéphaneChazelas Obrigado, mas desde que o Xorg foi iniciado -nolisten tcp, não há soquete TCP. Se tudo falhar, provavelmente voltarei a usar o xscope ou seu truque puro strace + text2pcap. Eu ainda estaria interessado em uma captura genérica de soquete Unix (apenas dados, não dados de canal lateral).
Lekensteyn
Além do strace, você também pode olhar para auditd e systemtap.
Stéphane Chazelas
O systemtap quase parece um hack do GDB, mas depois no nível do kernel. Não sei sobre auditoria, só encontrei um gancho LSM que verificava se você tem permissão para ler / gravar. (Atualmente, estou cavando no código-fonte do kernel Linux)
Lekensteyn

Respostas:

12

No kernel do Linux v4.2-rc5, não é possível capturar diretamente usando as interfaces que estão em uso pelo libpcap. A libpcap usa o domínio específico do Linux AF_PACKET(alias PF_PACKET), que apenas permite capturar dados de dados passando por um "dispositivo de rede " (como interfaces Ethernet).

Não há interface do kernel para captura de AF_UNIXsoquetes. As capturas Ethernet padrão têm um cabeçalho Ethernet com origem / destino, etc. Os soquetes Unix não possuem esse cabeçalho falso e o registro de tipos de cabeçalho da camada de link não lista nada relacionado a isso.

Os pontos de entrada básicos para dados são unix_stream_recvmsge unix_stream_sendmsgpara SOCK_STREAM( SOCK_DGRAMe SOCK_SEQPACKETpossuem funções nomeadas de maneira semelhante). Os dados são colocados na sk->sk_receive_queuee na unix_stream_sendmsgfunção , não existe um código que, finalmente, levam a chamar a tpacket_rcvfunção para captura de pacotes. Veja esta análise da osgx no SO para obter mais detalhes sobre os aspectos internos da captura de pacotes em geral.

De volta à pergunta original sobre AF_UNIXmonitoramento de soquete, se você estiver interessado principalmente nos dados do aplicativo, você tem algumas opções:

  • Passivo (também funciona para processos já em execução):
    • Use stracee capture em possíveis chamadas do sistema que executam E / S. Há muitos deles, read, pread64, readv, preadv, recvmsge muitos mais ... Ver @ Stéphane Chazelas exemplo para xterm. A desvantagem dessa abordagem é que você primeiro precisa encontrar o descritor de arquivo e ainda assim pode perder chamadas do sistema. Com o strace, você pode usar -e trace=filea maioria deles ( preadé coberto apenas por -e trace=desc, mas provavelmente não é usado para soquetes Unix pela maioria dos programas).
    • Break on / modificar unix_stream_recvmsg, unix_stream_sendmsg(ou unix_dgram_*ou unix_seqpacket_*) no núcleo e saída dos dados, em algum lugar. Você pode usar o SystemTap para definir esses pontos de rastreio. Aqui está um exemplo para monitorar mensagens de saída. Requer suporte ao kernel e disponibilidade de símbolos de depuração .
  • Ativo (funciona apenas para novos processos):

    • Use um proxy que também grave arquivos. Você mesmo pode escrever um multiplexador rápido ou hackear algo assim que também gera um pcap (cuidado com as limitações, por exemplo, AF_UNIXpode passar descritores de arquivos, AF_INETnão pode):

      # 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 um proxy de aplicativo dedicado. Para o X11, existe o xscope ( git , manual ).

CONFIG_UNIX_DIAGInfelizmente, a opção sugerida também não é útil aqui, só pode ser usada para coletar estatísticas, não para adquirir dados em tempo real conforme eles fluem (consulte linux / unix_diag.h ).

Infelizmente, não há rastreadores perfeitos no momento para soquetes de domínio Unix que produzem pcaps (que eu saiba). Idealmente, haveria um formato libpcap que tenha um cabeçalho contendo o PID de origem / destino (quando disponível) seguido por dados adicionais opcionais (credenciais, descritores de arquivo) e, finalmente, os dados. Na falta disso, o melhor que pode ser feito é o rastreamento de syscall.


Informações adicionais (para o leitor interessado), aqui estão alguns backtraces (adquiridos com o GDB interrompendo unix_stream_*e rbreak packet.c:., Linux no QEMU e socat no mainline Linux 4.2-rc5):

# 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
fonte
A propósito, se você leu kristrev.github.io/2013/07/26/… e viu instruções para procurar notificações de link via netlink e se perguntou se o diagnóstico pode fornecer detecção de pacotes, a resposta ainda é não . Esses diagnósticos fornecem estatísticas por meio de pesquisa, não em tempo real.
Lekensteyn
9

Eu escrevi uma ferramenta para capturar e despejar o tráfego de soquete de domínio unix. Ele usa bpf/kprobepara analisar a função do kernel unix_stream_sendmsge despejar o tráfego no espaço do usuário.

A ferramenta depende bcc, então você precisa instalar bccprimeiro.

Um exemplo de execução:

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