hana_shinのLinux技術ブログ

Linuxの技術情報を掲載しています。特にネットワークをメインに掲載していきます。

マルチキャストプログラミング



1 はじめに

マルチキャストパケットの送受信プログラムを作成して、次のことを確認してみます。
マルチキャストIPv4アドレスとマルチキャストMACアドレスの関係
・IGMP Join/Leaveメッセージ送信契機
マルチキャストアドレスの確認方法(ipコマンド)
マルチキャストパケットのフォーマット(Wiresharkで確認)

2 検証環境

2.1 ネットワーク構成

サーバとクライアントの2台構成です。図中のeth0はNICの名前です。

                           192.168.122.0/24
client(eth0) ------------------------------------------(eth0) server
        .181                                           .225

2.2 版数

CentOS版数は、サーバ、クライアントともに下記版数です。

[root@agent ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

カーネル版数は以下のとおりです。

[root@agent ~]# uname -r
3.10.0-1160.el7.x86_64

3 マルチキャストアドレスのマッピング方法

マルチキャストアドレスは、マルチキャストIPv4アドレスとマルチキャストMACアドレスがあります。マルチキャストIPv4アドレスは、以下のようにマルチキャストMACアドレスマッピングされます。

                          224         10           1           1
                      +----------+-----------+----------+----------+
                      | 11100000 |0 000 1010 | 00000001 | 00000001 | マルチキャストIPv4アドレス
                      +----------+-----------+----------+----------+
                                    |                              |
                                      下位23ビットをそのままマッピング
                                    |                              |
                                    V                              V
+----------+----------+----------+-----------+----------+----------+
| 00000001 | 00000000 | 01011110 |0 000 1010 | 00000001 | 00000001 | マルチキャストMACアドレス
+----------+----------+----------+-----------+----------+----------+
    01          00         5E     A
                                  |
                                  |
                                  0固定

4 サンプルプログラム

サーバとクライアントでそれぞれプログラムを作成します。

4.1 サーバプログラム

サーバプログラムは以下になります。サーバプログラムを実行すると、UDPの11111番ポートでパケットの受信待ちをします。また、宛先IPv4マルチキャストアドレスが239.0.0.100のパケットを受信します。

[root@server ~]# cat sv.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main(int argc, char *argv[])
{
  int sock;
  struct sockaddr_in addr;
  struct ip_mreq mreq;

  char buf[2048];

  sock = socket(AF_INET, SOCK_DGRAM, 0);

  addr.sin_family = AF_INET;
  addr.sin_port = htons(11111);
  addr.sin_addr.s_addr = INADDR_ANY;

  bind(sock, (struct sockaddr *)&addr, sizeof(addr));

  memset(&mreq, 0, sizeof(mreq));
  mreq.imr_interface.s_addr = inet_addr("192.168.122.225");
  mreq.imr_multiaddr.s_addr = inet_addr("239.0.0.100");

  if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) != 0) {
    perror("setsockopt");
    return 1;
 }

  memset(buf, 0, sizeof(buf));
  recv(sock, buf, sizeof(buf), 0);

  printf("%s\n", buf);

  close(sock);
  return 0;
}

サーバプログラムをコンパイルします。

[root@server ~]# gcc -Wall -o sv sv.c

4.2 クライアントプログラム

クライアントプログラムは、宛先が239.0.0.100のマルチキャストパケットを送信します。

[root@client ~]# cat cl.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

int main(int argc, char *argv[])
{
  int sock;
  struct sockaddr_in addr;
  struct sockaddr_in src_addr;
  in_addr_t ipaddr;

  sock = socket(AF_INET, SOCK_DGRAM, 0);
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(11111);
  addr.sin_addr.s_addr = inet_addr("239.0.0.100");

  src_addr.sin_addr.s_addr = inet_addr("192.168.122.181");
  if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (char *)&src_addr.sin_addr.s_addr, sizeof(ipaddr)) != 0) {
    perror("setsockopt");
    return 1;
  }

  if(sendto(sock, "HELLO", 5, 0, (struct sockaddr *)&addr, sizeof(addr)) == -1){
    perror("sendto");
    return 1;
  }
  close(sock);
  return 0;
}

クライアントプログラムをコンパイルします。

[root@client ~]# gcc -Wall -o cl cl.c

5 IGMP Join/Leaveメッセージ送信契機

サーバプログラムを実行すると、setsockoptシステムコールが実行されます。setsockoptシステムコールを実行すると、IGMP Joinパケットが送信されます。また、サーバプログラムが終了すると、IGMP Leaveパケットが送信されます。この時、サーバで採取したWiresharkを以下に示します。なぜか、2つずつパケットが送信されています。現時点で原因はわかりません。
f:id:hana_shin:20220324193949p:plain

6 マルチキャストアドレスの確認

サーバプログラムを実行すると、インタフェースにマルチキャストMACアドレスが設定されます。ipコマンドを使うと、マルチキャストアドレスを確認することができます。

まず、サーバプログラムを実行する前のインタフェースの状態を確認します。

[root@server ~]# ip maddress show dev eth0
1:      lo
        inet  224.0.0.1
2:      eth0
        link  01:00:5e:00:00:01
        link  33:33:00:00:00:01
        link  33:33:ff:84:02:b1
        inet  224.0.0.1
        inet6 ff02::1:ff84:2b1
        inet6 ff02::1
        inet6 ff01::1

サーバプログラムを実行します

[root@server ~]# ./sv

サーバプログラムを実行したあとのインタフェース(eth0)を確認します。マルチキャストMACアドレス01:00:5e:00:00:64(●)がインタフェースに追加されたことがわかります。このアドレスは、239.0.0.100(★)に対応したマルチキャストMACアドレスです。

[root@server ~]# ip maddress show dev eth0
1:      lo
        inet  224.0.0.1
2:      eth0
        link  01:00:5e:00:00:01
        link  33:33:00:00:00:01
        link  33:33:ff:84:02:b1
        link  01:00:5e:00:00:64 ●
        inet  239.0.0.100 ★
        inet  224.0.0.1
        inet6 ff02::1:ff84:2b1
        inet6 ff02::1
        inet6 ff01::1

7 マルチキャストパケットの送受信確認

サーバプログラムは、UDPの11111番ポートで受信パケットを待ち受けるので、11111番ポートを解放します。なお、firewall-cmdコマンドの使い方は、firewall-cmdの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# firewall-cmd --add-port=11111/udp
success

ポート番号を確認します。UDPの11111番ポートが解放されたことがわかります。

[root@server ~]# firewall-cmd --list-ports
11111/udp

サーバでtcpdumpを実行します。なお、tcpdumpの使い方は、tcpdumpの使い方(基本編) - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# tcpdump -i eth0 port 11111 or igmp -w igmp.pcap

サーバプログラムを実行します。

[root@server ~]# ./sv

サーバプログラムが受信パケットを待ち受けているポートを確認します。UDPの11111番ポートで受信パケットを待ち受けていることがわかります。なお、lsofコマンドの使い方は、lsofコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# lsof -c sv -a -i -a -nP
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sv      1886 root    3u  IPv4  23521      0t0  UDP *:11111

クライアントプログラムを実行します。

[root@client ~]# ./cl
[root@client ~]#

クライアントプログラムが送信した文字列(HELLO)がサーバプログラムで受信できたことがわかります。

[root@server ~]# ./sv
HELLO
[root@server ~]#

Ctrl+Cを押下してtcpdumpを終了します。

[root@server ~]# tcpdump -i eth0 port 11111 or igmp -w igmp1.pcap
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
^C5 packets captured
5 packets received by filter
0 packets dropped by kernel

サーバで採取したtcpdumpを確認します。クライアントプログラムが送信したマルチキャストパケットの宛先IPアドレスは239.0.0.100であることがわかります。また、その時の宛先マルチキャストMACアドレスは、01:00:5e:00:00:64であることがわかります。01:00:5e:00:00:64は、サーバプログラムがNICに設定したマルチキャストMACアドレスになります。
f:id:hana_shin:20220324203540p:plain

Z 参考図書

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ