IPv6 ユニークローカルユニキャストアドレスのプレフィクス部の計算ツール

家庭内 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 は問題ありありですね 🙁


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です