C# でIPアドレスの「仮承諾」や「優先」などの状態を取得する

C#

背景

C# のアプリ内で、netsh を外部プロセスでコールしてIPアドレスを変更する処理をしていたのですが、プロセスを同期処理にして処理が返ってきても、IPアドレスを変更した瞬間は状態が「仮承諾」となっていて通信が不可になる現象が発生しました。

この「仮承諾」状態は、内部で5秒程度で「優先」に変わって、以降は通信が可能になるようです。

しかし、アプリ的に netsh でIPアドレスを変更する処理が完了して、いつから通信開始していいのか判断する必要があるので「優先になったタイミングを知りたい」ということになりました。

ということで、首題の通り、IPアドレスの状態を取得する方法を調べたので、備忘録で残しておきます。

実装

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;

namespace IpAddressWork.Core
{
    static public class IPNetwork
    {
        /// <summary>
        /// マルチキャストのIPアドレス(IPv4のみ)の状態を取得する
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<(IPAddress address, DuplicateAddressDetectionState state)> GetMulticastIPv4AddressInformationCollection()
        {
            foreach (var nif in GetEthernetIPv4Properties())
            {
                var multicastIPAddressInformationCollection = nif.MulticastAddresses;

                foreach (var multicastIPAddressInformation in multicastIPAddressInformationCollection)
                {
                    if (multicastIPAddressInformation.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                    {
                        yield return (multicastIPAddressInformation.Address, multicastIPAddressInformation.DuplicateAddressDetectionState);
                    }
                }
            }
        }

        /// <summary>
        /// ユニキャストのIPアドレス(IPv4のみ)の状態を取得する
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<(IPAddress address, DuplicateAddressDetectionState state)> GetUnicastIPv4AddressInformationCollection()
        {
            foreach (var nif in GetEthernetIPv4Properties())
            {
                var unicastIPAddressInformationCollection = nif.UnicastAddresses;

                foreach (var unicastIPAddressInformation in unicastIPAddressInformationCollection)
                {
                    if (unicastIPAddressInformation.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                    {
                        yield return (unicastIPAddressInformation.Address, unicastIPAddressInformation.DuplicateAddressDetectionState);
                    }
                }
            }
        }

        static IEnumerable<IPInterfaceProperties> GetEthernetIPv4Properties()
        {
            return NetworkInterface.GetAllNetworkInterfaces()
                // イーサネットでフィルター
                .Where(i => i.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
                .Select(i => i.GetIPProperties())
                // ここでは IPv4 でフィルタリングしています。必要に応じて IPv6 にしたりしてください
                .Where(e => e.GetIPv4Properties() != null);
        }
    }
}

上記の GetMulticastIPv4AddressInformationCollection メソッドにて、IPアドレスと状態のタプルのリストが取得できます。

状態は DuplicateAddressDetectionState で表されており、Tentative が「仮承諾」、Preferred が「優先」に該当します。
(詳細は公式を参照)

例えば以下のような感じで状態を監視して、「優先」状態になったことを判断できるようになりました。

//「仮承諾」状態だと通信が不可なので、「優先」になるまで待つ
await Observable.Interval(TimeSpan.FromMilliseconds(100)).Where(x =>
{
    return GetUnicastIPv4AddressInformationCollection().All(i => i.state == DuplicateAddressDetectionState.Preferred);
})
.FirstAsync().ToTask();

参考

コメント

タイトルとURLをコピーしました