IPv6 によるマルチキャスト

## 1. 概要
この記事では、IPv6 によるマルチキャストの実現方法を Golang のサンプルコードで例示します。

## 2. はじめに
前回の記事 “[IPv4 によるマルチキャスト](https://qiita.com/KyojiOsada/items/bc381b3058715d4011d2)” では、IPv4 によるマルチキャストの実現方法を Golang サンプルコードで例示しました。
今回の記事では、IPv6 によるマルチキャストを取り上げます。
なぜ、IPv4 ではなく、わざわざ IPv6 を利用するのか？
それは、ネットワーク設計の要件によっては、IPv6 のエニーキャストも、とても有用であるため、IPv6 のエニーキャストについて投稿する前に、IPv6 のマルチキャストの実現方法について取り上げました。
マルチキャスト、エニーキャスト、ユニキャスト、ブロードキャストなど、それぞれの意味については下記のリンクをご参照ください。

* [マルチキャストとは？](https://ja.wikipedia.org/wiki/%E3%83%9E%E3%83%AB%E3%83%81%E3%82%AD%E3%83%A3%E3%82%B9%E3%83%88)
* [エニーキャストとは？](https://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%8B%E3%83%BC%E3%82%AD%E3%83%A3%E3%82%B9%E3%83%88)
* [ユニキャストとは？](https://ja.wikipedia.org/wiki/%E3%83%A6%E3%83%8B%E3%82%AD%E3%83%A3%E3%82%B9%E3%83%88)
* [ブロードキャストとは？](https://ja.wikipedia.org/wiki/%E3%83%96%E3%83%AD%E3%83%BC%E3%83%89%E3%82%AD%E3%83%A3%E3%82%B9%E3%83%88)

また、UDP やマルチキャストの有用性については、前回の記事 “[IPv4 によるマルチキャスト](https://qiita.com/KyojiOsada/items/bc381b3058715d4011d2)” をご参照ください。
今回は、前回と同じように、マルチキャスト送信サーバー x 1、マルチキャスト受信サーバー x 2 のミニマム構成により、マルチキャストの実現方法を Golang サンプルコードで例示します。

## 3. 環境
* RHEL-7 系
* Go 1.9

## 4. 設計
* IPv6 マルチキャスト送信側 x 1
    * sender1（[ff02::1%eth0]:56789）
* IPv6 マルチキャスト受信側 x 2
    * receiver1（[ff02::1]:56789）
    * receiver2（[ff02::1]:56789）

## 5. IPv6 マルチキャストアドレス
IPv6 では、`ff00::/8` ブロックにあるアドレスを、マルチキャストアドレスと呼びます。
そして、そのレンジにある `ff02::/16` ブロックの IPv6 アドレスは、Link-Local マルチキャストアドレスといい、`02` によりそのスコープを Link-Local に限定する事で、単一リンク内（単一サブネット内）に対してのマルチキャストを可能としています。
更に、`ff02::1/128` アドレスの `::1` は、全ノードマルチキャストアドレスといい、対象マルチキャストグループの全ノードへのマルチキャストが可能となります。
（`::2` は、全ルータマルチキャストアドレスといい、対象マルチキャストグループの全ルータへの配送が可能）

この記事では、`ff02::1` のリンクローカルスコープの全ノードマルチキャストアドレスと `56789` ポートを例示に使用します。

## 6. Golang サンプルコード
@ sender1
`$ sudo nvim ~/go/ipv6_multicast_sender.go`

```go:~/go/ipv6_multicast_sender.go
package main

import (
	"fmt"
	"net"
	"strconv"
	"time"
)

func main() {
	ipv6 := "[ff02::1%eth0]"
	port := ":56789"
	addr := ipv6 + port
	wait_time := 1
	format := "Message "

	fmt.Println("Sender:", addr)
	conn, err := net.Dial("udp", addr)
	_error(err)
	defer conn.Close()

	c := 0
	for {
		time.Sleep(time.Duration(wait_time) * time.Second)
		message := format + strconv.Itoa(c)
		conn.Write([]byte(message))
		fmt.Printf("%s\n", message)
		c++
	}
}

func _error(_err error) {
	if _err != nil {
		panic(_err)
	}
}
```

@ receiver1, 2

`$ sudo nvim ~/go/ipv6_multicast_receiver.go`

```go:~/go/ipv6_multicast_receiver.go
package main

import (
	"fmt"
	"net"
)

func main() {
	ipv6 := "[ff02::1]"
	port := ":56789"
	addr := ipv6 + port
	fmt.Println("Receiver:", addr)
	udp_addr, err := net.ResolveUDPAddr("udp", addr)
	_error(err)

	listener, err := net.ListenMulticastUDP("udp", nil, udp_addr)
	_error(err)
	defer listener.Close()

	buffer := make([]byte, 1240)
	for {
		length, remoteAddress, err := listener.ReadFrom(buffer)
		_error(err)

		fmt.Printf("Sender: %v\n", remoteAddress)
		fmt.Printf("Contents: %s\n", string(buffer[:length]))
	}
}

func _error(_err error) {
	if _err != nil {
		panic(_err)
	}
}
```


## 7. Golang サンプル実行
@ receiver1, 2
`$ sudo go run ~/go/ipv6_multicast_receiver.go`

@ sender1
`$ sudo go run ~/go/ipv6_multicast_sender.go`


## 8. まとめ
この記事では、IPv6 によるマルチキャストの実現方法を Golang のサンプルコードで例示しました。
UDP マルチキャストは、効率的な死活監視設計や、自律ネットワーク設計、あるいは No Cluster データリソース設計など、要件や用途によっては有用な使い方が可能となりえます。
そして、IPv6 のエニーキャストも、ネットワーク設計の要件によってはとても有用であるため、IPv6 のエニーキャストについて投稿する前に、今回は IPv6 のマルチキャストの実現方法について取り上げました。