Lendo consultas MLDv2 usando um soquete IPv6

1

Eu tenho mrd6 instalado no meu pi de framboesa. Ele registra com uma interface local (tun0) e periodicamente transmite consultas MLDv2 sobre ele.

De acordo com [ RFC3810 ], os tipos de mensagem MLDv2 são um subconjunto de mensagens ICMPv6 e são identificados em pacotes IPv6 por um anterior próximo valor de cabeçalho de 58 (0x3a). Eles são enviados com um endereço de origem IPv6 de link local, um limite de salto IPv6 de 1 e uma opção de alerta de roteador IPv6 [ RFC2711 ] em um cabeçalho Opções Hop-by-Hop.

Posso confirmar que estou vendo esses pacotes periodicamente em tun0:

pi@machine:~ $ sudo tcpdump -i tun0 ip6 -vv -XX

01:22:52.125915 IP6 (flowlabel 0x71df6, hlim 1, next-header Options (0)
payload length: 36) 
fe80::69bf:be2d:e087:9921 > ip6-allnodes: HBH (rtalert: 0x0000) (padn)
[icmp6 sum ok] ICMP6, multicast listener query v2 [max resp delay=10000]
[gaddr :: robustness=2 qqi=125]
            0x0000:  6007 1df6 0024 0001 fe80 0000 0000 0000  '....$..........
            0x0010:  69bf be2d e087 9921 ff02 0000 0000 0000  i..-...!........
            0x0020:  0000 0000 0000 0001 3a00 0502 0000 0100  ........:.......
            0x0030:  8200 b500 2710 0000 0000 0000 0000 0000  ....'...........
            0x0040:  0000 0000 0000 0000 027d 0000            .........}..

Eu tenho um soquete configurado no meu aplicativo no tun0 da seguinte forma, já que espero que sejam pacotes ICMP:

int fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); // ICMP

// ... bind this socket to tun0

  int interfaceIndex = // tun0 interface Index
  int mcastTTL = 10;
  int loopBack = 1;

  if (setsockopt(listener->socket,
                 IPPROTO_IPV6,
                 IPV6_MULTICAST_IF,
                 &interfaceIndex,
                 sizeof(interfaceIndex))
      < 0) {
    perror("setsockopt:: IPV6_MULTICAST_IF:: ");
  }

  if (setsockopt(listener->socket,
                 IPPROTO_IPV6,
                 IPV6_MULTICAST_LOOP,
                 &loopBack,
                 sizeof(loopBack))
      < 0) {
    perror("setsockopt:: IPV6_MULTICAST_LOOP:: ");
  }

  if (setsockopt(listener->socket,
                 IPPROTO_IPV6,
                 IPV6_MULTICAST_HOPS,
                 &mcastTTL,
                 sizeof(mcastTTL))
      < 0) {
    perror("setsockopt:: IPV6_MULTICAST_HOPS::  ");
  }

  struct ipv6_mreq mreq6 = {{{{0}}}};
  MEMCOPY(&mreq6.ipv6mr_multiaddr.s6_addr, sourceAddress, 16);
  mreq6.ipv6mr_interface = interfaceIndex;

  if (setsockopt(listener->socket,
                 IPPROTO_IPV6,
                 IPV6_JOIN_GROUP,
                 &mreq6,
                 sizeof(mreq6))
      < 0) {
    perror("setsockopt:: IPV6_JOIN_GROUP::  ");
  }

Configurando o soquete dessa maneira, posso receber solicitações de eco ICMP, respostas para o meu próprio endereço e multicast enviadas usando o endereço multicast link-local. No entanto, não vejo nenhuma consultas MLDv2.

Aqui está o meu ciclo de recebimento:

  uint8_t received[1000] = { 0 };
  struct sockaddr_storage peerAddress = { 0 };
  socklen_t addressLength = sizeof(peerAddress);
  socklen_t addressLength = sizeof(peerAddress);

  int receivedLength = recvfrom(sockfd,
                                received,
                                sizeof(received),
                                0,
                                (struct sockaddr *)&peerAddress,
                                &addressLength);

  if (receivedLength > 0) {
    // Never get here for MLDv2 queries.
  }

Pesquisando isso um pouco mais, descobri a opção de soquete IPV6_ROUTER_ALERT, que a página man descreve da seguinte forma:

IPV6_ROUTER_ALERT
Pass forwarded packets containing a router alert hop-by-hop option to this socket.
Only allowed for SOCK_RAW sockets.  The tapped packets are not forwarded by the
kernel, it is the user's responsibility to send them out again.  Argument is a
pointer to an integer.  A positive integer indicates a router alert option value
to intercept.  Packets carrying a router alert option with a value field
containing this integer will be delivered to the socket.  A negative integer
disables delivery of packets with router alert options to this socket.

Então, percebi que estava faltando essa opção e tentei defini-la da seguinte maneira. [ RFC2710 ] 0 significa mensagem de descoberta de escuta de difusão seletiva.

  int routerAlertOption = 0;

  if (setsockopt(listener->socket,
                 IPPROTO_IPV6,
                 IPV6_ROUTER_ALERT,
                 &routerAlertOption,
                 sizeof(routerAlertOption))
      < 0) {
    perror("setsockopt:: IPV6_ROUTER_ALERT::  ");
  }

No entanto, isso me dá o erro ENOPROTOOPT (errno 92). Um pouco mais de googling ( link ) me levou ao fato de que você pode é possível definir a opção IPV6_ROUTER_ALERT com o protocolo IPPROTO_ICMPV6. Precisa de um soquete definido usando o protocolo IPPROTO_RAW.

No entanto, definindo meu soquete como:

int fd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);

significa que não consigo mais receber nenhum pacote ICMP na minha conta.

TL; DR: Como leio consultas MLDv2 usando um soquete IPv6?

editar (resposta): Parece que as implementações convencionais do Linux eliminarão os pacotes MLDv2 ao passá-los para um soquete ICMPV6. Por que isso é, não tenho certeza. (Pode ser por causa da opção do próximo cabeçalho.)

Eu fui com uma abordagem de leitura de pacotes brutos na interface tun0. Eu segui o exemplo do ping6_ll.c aqui: link .

Ele usa um soquete com (SOCK_RAW, ETH_P_ALL). Você também pode definir algumas opções de SOL_PACKET para filtrar regras específicas de multicast na sua interface.

    
por Suvesh Pratapa 10.11.2017 / 15:34

0 respostas