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インターフェイスからrawパケットを読み取る方法を使用しました。ここではping6_ll.cの例に従います。http://www.pdbuchan.com/rawsock/rawsock.html

(SOCK_RAW, ETH_P_ALL) ソケットを使用します。インターフェイスで特定のマルチキャストルールをフィルタリングするために、いくつかのSOL_PACKETオプションを設定することもできます。

ベストアンサー1

おすすめ記事