家庭内 LAN で IPv6 のユニークローカルユニキャストアドレスを使ってみようかと思い立って、RFC 4193 Unique Local IPv6 Unicast Addresses の 3.2.2. Sample Code for Pseudo-Random Global ID Algorithm に沿った計算を行うツールを C# と WPF で作ってみました。ソースコードとプログラムは MAKCRAFT のページで公開したので、ここでは計算するために作った Ipv6Util クラスを書いておきます(他の部分は UI 関係になるので 🙂 )。
計算を行うメソッドだけで、情報の保存などはないことから、static class にしています。また、タイムスタンプ計算や NTP タイムスタンプの 2036 年の桁溢れに対応するための定数等を定義しています(このツールではメソッド内のローカル変数で構わない構成になるのですが、別途作った SNTP ツールのコードから流用しているので、こうなっています 😉 )。
//
public static class Ipv6Util
{
private const long UNIT_RATING32 = 0x100000000L; // (2 ** 32)
private static readonly DateTime START_DATETIME = new DateTime(1900, 1, 1, 0, 0, 0); // 2036年の桁あふれまで
private static readonly DateTime PASSED_START_DATETIME = START_DATETIME.AddSeconds(uint.MaxValue); // 2036年以降
//
/// <summary>
/// ローカルコンピュータ上のイーサネットのネットワーク・アダプターの数を取得する
/// </summary>
public static int NumberOfNic
{
get
{
return getEthernetInterfaces().Count();
}
}
//
/// <summary>
/// ローカルコンピュータのイーサネット・インターフェイスのコレクションを取得する
/// </summary>
/// <returns></returns>
private static IEnumerable<System.Net.NetworkInformation.NetworkInterface> getEthernetInterfaces()
{
var networkInterfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
return networkInterfaces
.Where((n) => n.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Ethernet);
}
//
/// <summary>
/// DateTime(UTC) を使用して NTP タイムスタンプの始点秒数を取得する
/// </summary>
/// <param name="datetime"></param>
/// <returns></returns>
private static DateTime GetStartingDatetime(DateTime datetime)
{
return PASSED_START_DATETIME <= datetime ? PASSED_START_DATETIME : START_DATETIME;
}
//
/// <summary>
/// IPv6 アドレス(16ビット単位表記(先行0省略))から16ビット単位表記(先行0省略・連続0省略)を取得する
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
private static string shortenString(string source)
{
var patternStr = "0:0:0:0:0:0:0:0";
var checkStr = ":-:";
var val = "";
for (int i = 7; i > 0; i--)
{
var rgx = new System.Text.RegularExpressions.Regex(patternStr);
var m = rgx.Match(source);
if (m.Success)
{
val = rgx.Replace(source, "-", 1);
if (val == "-")
val = "::";
else
{
var chkRgx = new System.Text.RegularExpressions.Regex(checkStr);
var mCheck = chkRgx.Match(val);
if (mCheck.Success)
val = chkRgx.Replace(val, "::");
else
val = val.Replace("-", ":");
}
break;
}
else
patternStr = patternStr.Substring(2);
}
return val == "" ? source : val;
}
//
/// <summary>
/// DateTime(UTC) 型から NTP 64ビット固定小数点数(タイムスタンプ)形式へ変換する
/// </summary>
/// <param name="datetime"></param>
/// <returns></returns>
public static ulong Datetime2NtpTimeStamp(DateTime datetime)
{
var startDatetime = GetStartingDatetime(datetime);
var milliseconds = (ulong)(datetime - startDatetime).TotalMilliseconds;
var seconds = (uint)(milliseconds / 1000);
var fraction = (uint)((milliseconds % 1000) * UNIT_RATING32 / 1000);
return (ulong)(((ulong)seconds << 32) | fraction);
}
//
/// <summary>
/// ローカルコンピュータの MAC アドレスを一つ取得する
/// </summary>
/// <returns></returns>
public static byte[] GetMacAddress()
{
return getEthernetInterfaces()
.First().GetPhysicalAddress().GetAddressBytes();
}
/// <summary>
/// ローカルコンピュータの MAC アドレスから EUI-64 識別子を取得する
/// </summary>
/// <returns></returns>
public static byte[] GetEui64()
{
return GetEui64(GetMacAddress());
}
/// <summary>
/// MAC アドレスから EUI-64 識別子を取得する
/// </summary>
/// <param name="macAddress">MAC アドレス</param>
/// <returns></returns>
public static byte[] GetEui64(byte[] macAddress)
{
var eui64Array = new byte[8];
Array.Copy(macAddress, eui64Array, 3);
eui64Array[3] = 0xFF;
eui64Array[4] = 0xFE;
Array.Copy(macAddress, 3, eui64Array, 5, 3);
eui64Array[0] = (byte)(eui64Array[0] ^ 0x02);
return eui64Array;
}
//
/// <summary>
/// NTP タイムスタンプと EUI-64 識別子から SHA-1 ダイジェストを計算するためのシードを取得する
/// </summary>
/// <param name="timeStamp"></param>
/// <param name="eui64"></param>
/// <returns></returns>
public static byte[] GetSeed(ulong timeStamp, byte[] eui64)
{
if (eui64.Length != 8 )
throw new ArgumentException("引数(eui64)の長さが 64bit ではありませんでした。");
var timeStampArray = BitConverter.GetBytes(timeStamp);
if (BitConverter.IsLittleEndian) // ホストバイトオーダーがリトルエンディアンだったら
Array.Reverse(timeStampArray); // ビッグエンディアンへ変換する
return timeStampArray.Concat(eui64).ToArray();
}
//
/// <summary>
/// シードから SHA-1 ダイジェストを取得する
/// </summary>
/// <param name="seed"></param>
/// <returns></returns>
public static byte[] GetSha1Digest(byte[] seed)
{
if (seed.Length != 16)
throw new ArgumentException("引数(seed)の長さが 128bit ではありませんでした。");
var sha = new System.Security.Cryptography.SHA1CryptoServiceProvider();
return sha.ComputeHash(seed);
}
//
/// <summary>
/// SHA-1 ダイジェストからグローバルIDを取得する
/// </summary>
/// <param name="sha1Digest"></param>
/// <returns></returns>
public static byte[] GetGlobalId(byte[] sha1Digest)
{
if (sha1Digest.Length != 20)
throw new ArgumentException("引数(sha1Digest)の長さが 160bit ではありませんでした。");
var val = new byte[5];
Array.Copy(sha1Digest, 15, val, 0, val.Length);
return val;
}
//
/// <summary>
/// グローバルIDからユニークローカルユニキャストアドレスのルーティングプレフィクスを取得する
/// </summary>
/// <param name="globalId"></param>
/// <returns></returns>
public static byte[] GetRoutingPrefixUniqueLocalUnicast(byte[] globalId)
{
if (globalId.Length != 5)
throw new ArgumentException("引数(globalId)の長さが 40bit ではありませんでした。");
var uniqueLocalPrefix = new byte[1] { 0xFD };
return uniqueLocalPrefix.Concat(globalId).ToArray();
}
//
/// <summary>
/// IPv6 アドレス(byte 型配列)から16ビット単位表記(先行0省略)を取得する
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string bytes2Ip6DisplayString(byte[] bytes)
{
var val = new StringBuilder();
for (int i = 0; i < bytes.Length / 2; i++)
{
var temp = (ushort)bytes[i * 2] << 8 | bytes[i * 2 + 1];
if (i == 0)
val.Append(string.Format("{0:x}", temp));
else
val.Append(string.Format(":{0:x}", temp));
}
var valStr = val.ToString();
return val.ToString();
}
//
/// <summary>
/// IPv6 アドレス(byte 型配列)から16ビット単位表記(先行0省略・連続0省略)を取得する
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string bytes2Ip6DisplayShortenString(byte[] bytes)
{
return shortenString(bytes2Ip6DisplayString(bytes));
}
ドキュメンテーションコメントを入れているので、それぞれのメソッドの説明は省きました。
ユニークローカルユニキャストアドレスの設定は Windows7, Vista では問題なさそうですけど、XP は問題ありありですね 🙁