IPv6 소켓을 사용하여 MLDv2 쿼리 읽기

IPv6 소켓을 사용하여 MLDv2 쿼리 읽기

나는 가지고있다MRD6내 라즈베리 파이에 설치되었습니다. 로컬 인터페이스(tun0)에 등록하고 주기적으로 MLDv2 쿼리를 전송합니다.

에 따르면 [RFC3810], MLDv2 메시지 유형은 ICMPv6 메시지의 하위 집합이며 IPv6 패킷에서 이전 다음 헤더 값 58(0x3a)로 식별됩니다. 링크-로컬 IPv6 소스 주소, IPv6 홉 제한 1 및 IPv6 라우터 경고 옵션[RFC2711] 홉별 옵션 헤더에 있습니다.

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            .........}..

ICMP 패킷이 되기를 원하기 때문에 아래와 같이 tun0의 애플리케이션에 소켓을 설정했습니다.

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::  ");
  }

이런 방식으로 소켓을 설정하면 ICMP 에코 요청을 받을 수 있고, 내 주소에 응답할 수 있으며, 링크 로컬 멀티캐스트 주소를 사용하여 멀티캐스트를 보낼 수 있습니다. 그러나 나는 보지 못했다.어느MLDv2 쿼리.

이것은 내 수신 루프입니다.

  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.
  }

추가 조사 후에 매뉴얼 페이지에서 다음과 같이 설명하는 IPV6_ROUTER_ALERT 소켓 옵션을 발견했습니다.

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.

그래서 이 옵션을 놓쳤다고 생각하고 아래와 같이 설정을 해보았습니다. [RFC2710] 0은 멀티캐스트 수신기 발견 메시지를 나타냅니다.

  int routerAlertOption = 0;

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

그러나 이로 인해 ENOPROTOOPT 오류(errno 92)가 발생합니다. 더 많은 Google 검색(http://www.atm.tut.fi/list-archive/usagi-users-2005/msg00317.html) IPPROTO_ICMPV6 프로토콜을 사용하여 IPV6_ROUTER_ALERT 옵션을 설정할 수 없다는 것을 깨달았습니다. IPPROTO_RAW 프로토콜을 사용하여 정의된 소켓이 필요합니다.

그러나 내 소켓을 다음과 같이 정의합니다.

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

즉, 더 이상 recvfrom에서 ICMP 패킷을 수신할 수 없습니다.


핵심요약: IPv6 소켓을 사용하여 MLDv2 쿼리를 읽는 방법은 무엇입니까?


편집 (답변): Linux의 기존 구현에서는 MLDv2 패킷을 ICMPV6 소켓으로 전달할 때 삭제하는 것으로 보입니다. 왜 그런지 잘 모르겠습니다. (아마도 다음 타이틀 옵션 때문인 것 같습니다.)

tun0 인터페이스에서 원시 패킷을 읽는 방법을 사용했습니다. 여기 ping6_ll.c 예제를 따르고 있습니다.http://www.pdbuchan.com/rawsock/rawsock.html.

(SOCK_RAW, ETH_P_ALL) 소켓을 사용합니다. 인터페이스에서 특정 멀티캐스트 규칙을 필터링하기 위해 일부 SOL_PACKET 옵션을 설정할 수도 있습니다.

관련 정보