NTT情報流通プラットフォーム研究所
加藤淳也
はじめに
第5回までのシリーズを通してTCPを使った基本的なプログラムの作成方法を学びました。今回はこれまで扱わなかったトピックであるアダプタ(インターフェイス)が持つ詳細情報を取得するAPIを紹介します。アダプタ情報の例として、そこに割り当てられたIPv6アドレスがあげられます。また作成するプログラムによっては自分自身にどんなアダプタが装着されており、それぞれのアダプタにどのIPv6アドレスが割り当てられているか、取得する必要が生じるでしょう。今回紹介するAPIによってアダプタの詳細情報を取得できるようになります。詳細情報はIPv6アドレスに限らず、IPv4アドレスやMACアドレス、MTU値、アダプタのup/down状態など他の情報も含みます。
IP Helper API
概要IP Helper APIはWinSock APIを補助する機能がまとめられたユーティリティAPI郡です。WinSockの機能だけでは、冒頭で述べたアダプタの情報が取得できません。またルーティングテーブルの操作や、DNSクエリに関する機能、また本来RAWソケット使って操作を行うICMPv4/ICMPv6に関する機能をパッケージ化して提供しています。今回、解説の対象とする関数はアダプタ情報の取得関数の1つである「GetAdaptersAddresses()」関数です。
使用ヘッダとライブラリIP Helper APIを使うプログラムでは下記のように、ソースコード上でヘッダファイル「iphlpapi.h」を取り込みます。
#include ‹winsock2.h›
#include ‹ws2tcpip.h›
#include ‹iphlpapi.h› ←―― IP Helper APIに必要なヘッダファイル
.
.
またライブラリのリンクの際には、インポートライブラリとして「iphlpapi.lib」をリンクします。WinSock2のAPIとは独立した機構のためws2_32.libのリンクは必須ではありません。しかし両者のAPIはセットで使われることがほとんどのため、結果的に両者を同時にリンクすることになります。
C:\>cl v6programm.c ws2_32.lib iphlpapi.lib ← IP Helper API用インポートライブラリ
アダプタの詳細情報を取得するGetAdaptersAddresses()関数
GetAdaptersAddresses() APIの概要はじめにGetAdaptersAddresses()関数のプロトタイプを示します。
DWORD WINAPI GetAdaptersAddresses( ULONG Family, DWORD Flags, PVOID Reserved, PIP_ADAPTER_ADDRESSES pAdapterAddresses, PULONG pOutBufLen );
第1引数Familyによりアドレスファミリを限定することができます。アドレスに関する情報を取得する際に、AF_INET(IPv4のみを取得)、AF_INET6(IPv6のみを取得)、あるいはAF_UNSPEC(IPv4/IPv6両者を取得)の3つの指定が可能です。第2引数のFlagsも取得する情報の種類を選ぶもので、表1に示した値の組み合わせを指定することができます。フラグの内容の詳細は第4引数の解説の際に述べます。
表1: Flags変数に指定可能なパラメータ
|
第3引数のReservedは常にNULLを指定しておきます。第5引数はアダプタ情報を格納するのに必要なバッファサイズに関する情報です。アダプタやアドレスの構成によって、必要なバッファの領域が大きく異なります。そのためあらかじめプログラマが必要なバッファサイズを予想することは難しいです。そこで第4引数にNULLを指定してGetAdaptersAddresses()関数の呼び出しを行うと、情報を書き込むために必要なバッファのサイズをpOutBufLen変数に書き込んでくれます。そのサイズでバッファを確保して再度、第4引数に確保した領域を与えてGetAdaptersAddresses()関数を呼び出しアダプタ情報を書き込ませる使い方が想定されています。
第4引数のpAdapterAddressesのデータ構造はIP_ADAPTER_ADDRESSES型の構造体で表現されますので、次節でメンバを詳細に見ていくことにします。
またGetAdaptersAddresses()関数の戻り値は表2に示すとおりです。
表2: GetAdaptersAddresses()関数の戻り値
| ERROR_SUCCESS | 呼び出しが成功した |
| ERROR_BUFFER_OVERFLOW | アダプタ情報を書き出すメモリ領域が足りない。このときはpOutBufLenに、必要なメモリ領域の大きさが書き込まれるので、領域を確保後に再度GetAdaptersAddresses()関数を呼び出し情報を取得します |
| ERROR_INVALID_PARAMETER | pAdaterAddressesに情報を書き込めない。pOutBufLenに値を書き込めないなど誤ったポインタ指定を行った |
| ERROR_NOT_ENOUGH_MEMORY | GetAdaptersAddresses()を動作させるためのメモリが足りない。 ERROR_BUFFER_OVERFLOWとは違い致命的なエラーである |
| ERROR_SUCCESS以外のその他の値 | FormatMessage()関数によりエラー種別を取得してください |
IP_ADAPTER_ADDRESSES構造体
ヘッダファイル「iptypes.h」においてIP_ADAPTER_ADDRESSESの構造が定義されています。ヘッダファイル「iphlpapi.h」を取り込むと、このヘッダも自動的に取り込まれますので、プログラマが明示的にインクルードする必要はありません。
typedef struct _IP_ADAPTER_ADDRESSES {
union {
ULONGLONG Alignment;
struct {
ULONG Length;
DWORD IfIndex;
};
};
struct _IP_ADAPTER_ADDRESSES* Next;
PCHAR AdapterName;
PIP_ADAPTER_UNICAST_ADDRESS FirstUnicastAddress;
PIP_ADAPTER_ANYCAST_ADDRESS FirstAnycastAddress;
PIP_ADAPTER_MULTICAST_ADDRESS FirstMulticastAddress;
PIP_ADAPTER_DNS_SERVER_ADDRESS FirstDnsServerAddress;
PWCHAR DnsSuffix;
PWCHAR Description;
PWCHAR FriendlyName;
BYTE PhysicalAddress[MAX_ADAPTER_ADDRESS_LENGTH];
DWORD PhysicalAddressLength;
DWORD Flags;
DWORD Mtu;
DWORD IfType;
IF_OPER_STATUS OperStatus;
DWORD Ipv6IfIndex;
DWORD ZoneIndices[16];
PIP_ADAPTER_PREFIX FirstPrefix;
} IP_ADAPTER_ADDRESSES, *PIP_ADAPTER_ADDRESSES;
アダプタひとつに対してIP_ADAPTER_ADDRESSES構造体のインスタンスがひとつ生成されます。GetAdaptersAddresses()関数を呼んだホスト上に複数のアダプタが装着されている場合は、IP_ADAPTER_ADDRESSES構造体の連結リストが構成され、Nextメンバによって次のアダプタがポイントされます。なお最後のアダプタではNextメンバはNULLで終端されます。それではアダプタ情報の中身を順に見ていくことにします。
アダプタ名を格納するAdapterNameメンバPCHAR AdapterName;
AdapterName変数はアダプタを一意に特定する文字列が格納されます。ただし下記のようなUID形式での表記となります。レジストリには、この表記によりアダプタ情報が格納されています。
"{F1CA1BF8-B3BF-1FEE-99DB-FEA9C9B57B9CC}"
GUI上で見慣れた表記である「ローカルエリア接続」や「ワイヤレスネットワーク」のような通称はFriendlyNameメンバに格納されます。
アダプタの可読な名称を格納するFriendlyNameメンバと解説を格納するDescriptionメンバ PWCHAR FriendlyName;
PWCHAR Description;
アダプタの可読な名称がFriendlyNameに、解説がDescriptionに格納されます。前者はネットワークのプロパティを開いたときに表示されるネットワークアイコンの名称、また後者はアダプタのプロパティなどを開いたときに表示される解説です。下記はこれらのメンバの格納例です。
FriendlyName : "ローカル エリア接続" Description : "Intel(R) PRO/1000 MT Mobile Connection - パケット スケジューラミニポート"
注意点は、これらのメンバはPWCHAR型の変数として宣言されていることからワイド文字(ユニコード)でエンコードされて格納されていることです。必要に応じて文字コードを変換する必要があります。サンプルコードではWideByteToMultiByte()関数を使って、ユニコードからShift JISに変換して画面表示を行っています。
なお、GetAdaptersAddresses()関数の第2引数FlagsにGAA_FLAG_SKIP_FRIENDLY_NAMEを立てた場合はこのメンバは返却されません。
ユニキャストアドレスのリストを格納するFirstUnicastAddressメンバPIP_ADAPTER_UNICAST_ADDRESS FirstUnicastAddress;
アダプタに付与されさているユニキャストアドレスを格納するリストです。IP_ADAPTER_UNICAST_ADDRESS構造体のインスタンス一つに対して、一つのユニキャストアドレスの情報が格納されます。アダプタが複数のユニキャストアドレスを持っている場合はIP_ADAPTER_UNICAST_ADDRESS構造体の連結リストが構成されます。
GetAdaptersAddresses()関数の第1引数でアドレスファミリを限定するよう指定した場合は、指定したファミリのアドレスだけが並べられます。またGetAdaptersAddresses()関数の第2引数のFlagsにGAA_FLAG_SKIP_UNICASTを立てた場合はこのメンバには情報は書き込まれません。
IP_ADAPTER_UNICAST_ADDRESS構造体の定義
IP_ADAPTER_UNICAST_ADDRESS構造体は次のように定義されています。
typedef struct _IP_ADAPTER_UNICAST_ADDRESS {
union {
struct {
ULONG Length;
DWORD Flags;
};
};
struct _IP_ADAPTER_UNICAST_ADDRESS* Next;
SOCKET_ADDRESS Address;
IP_PREFIX_ORIGIN PrefixOrigin;
IP_SUFFIX_ORIGIN SuffixOrigin;
IP_DAD_STATE DadState;
ULONG ValidLifetime;
ULONG PreferredLifetime;
ULONG LeaseLifetime;
} IP_ADAPTER_UNICAST_ADDRESS, *PIP_ADAPTER_UNICAST_ADDRESS;
NULLで終端されたNextメンバでユニキャストアドレスの数だけ連結リストを作る点はIP_ADAPTER_ADDRESSES構造体と同じす。
IPアドレスを格納するAddressメンバAddressメンバはSOCKET_ADDRESS型の構造体であり、SOCKET_ADDRESS構造体のメンバlpSockaddr変数を通じてソケットアドレス形式のIPアドレスが取得できます。付随する情報としてソケットアドレスのサイズがiSockaddrLengthで取得可能です。
typedef struct _SOCKET_ADDRESS {
LPSOCKADDR lpSockaddr;
INT iSockaddrLength;
} SOCKET_ADDRESS, *PSOCKET_ADDRESS;
SOCKET_ADDRESS型の構造体もWinSock APIに固有の構造体で、ソケットアドレスの実体(lpSockaddr変数)へ行きつくまでの階層がやや冗長になっています。
アドレスの種類に関するPrefixOrigin, SuffixOriginメンバPrefixOrigin, SuffixOrigin変数は、アドレスのプリフィックス(ネットワーク)部とホスト部の取得元を表す変数です。アドレスをどのような手段で構成したか判別することができ、DHCPによる構成、ルータ広告による設定、匿名アドレス、あるいは手動設定を行ったか等がわかります。ヘッダファイル「iptypes.h」ではIP_PREFIX_ORIGIN, IP_SUFFIX_ORIGIN型は次のような列挙子によって定義されています。
typedef enum {
IpPrefixOriginOther = 0,
IpPrefixOriginManual,
IpPrefixOriginWellKnown,
IpPrefixOriginDhcp,
IpPrefixOriginRouterAdvertisement,
} IP_PREFIX_ORIGIN;
typedef enum {
IpSuffixOriginOther = 0,
IpSuffixOriginManual,
IpSuffixOriginWellKnown,
IpSuffixOriginDhcp,
IpSuffixOriginLinkLayerAddress,
IpSuffixOriginRandom,
} IP_SUFFIX_ORIGIN;
アドレスのプロトコルファミリと種別による、PrefixとSuffixの組み合わせは下記の通りです。
IPv4アドレスの場合
- 手動設定の場合
PrefixOrigin = IpPrefixOriginManual
SuffixOrigin = IpSuffixOriginManual - DHCPによる設定の場合
PrefixOrigin = IpPrefixOriginDhcp
SuffixOrigin = IpSuffixOriginDhcp
IPv4アドレスの場合はprefixとsuffixは同じ種類となります - ダイヤルアップ(PPP)で動的に割り当てられた場合
PrefixOrigin = IpPrefixOriginManual
SuffixOrigin = IpSuffixOriginManual - ループバックアドレス(127.0.0.1)
PrefixOrigin = IpPrefixManual
SuffixOrigin = IpSuffixManual
IPv6アドレスの場合
- 手動設定の場合
PrefixOrigin = IpPrefixOriginManual
SuffixOrigin = IpSuffixOriginManual - RAを受信しホスト部をMACアドレスからEUI-64で生成したアドレス
PrefixOrigin = IpPrefixOriginRouterAdvertisement
SuffixOrigin = IpSuffixOriginLinkLayerAddress - RAを受信しホスト部をランダムに生成した匿名アドレス
PrefixOrigin = IpPrefixOriginRouterAdvertisement
SuffixOrigin = IpSuffixOriginRandom - リンクローカルアドレスでホスト部をMACアドレスからEUI-64で生成したアドレス
PrefixOrigin = IpPrefixWellKnown
SuffixOrigin = IpSuffixOriginLinkLayerAddress - ループバックアドレス (::1)
PrefixOrigin = IpPrefixWellKnown
SuffixOrigin = IpSuffixWellKnown
アドレスの寿命を示すValidLifetime, PreferredLifetime, LeaseLifetimeメンバ
アドレスに有効期限がある場合は3つのULONG型のメンバに寿命値が設定されます。単位はすべて秒(sec)です
- ULONG ValidLifetime
- ULONG PreferredLifetime
- ULONG LeaseLifetime
- IPv4アドレスの場合
DHCPを使ってアドレスの払い出しを受けたときは、3つの変数はすべて同一の有効期限値が格納されるようです。 - IPv6アドレスの場合
プロトコル仕様で定められている最大有効期限(ValidLifetime)、推奨有効期限(PreferredLifetime)が格納されます。LeaseLifetimeは現在は使われている様子がありませんが、DHCPv6などのステートフルな手法でアドレスを割り当てた際に貸与期間を格納するために使用するメンバではないかと思われます。
DAD(Duplicated Address Detection)ステータスを格納するDadState
このメンバはIPv6アドレスの重複を検知する機構の状態を格納します。IPv4アドレスについてはDADの機構がありませんので、DadStateメンバの値は意味を持ちません。ヘッダファイル「iptypes.h」では5つの状態が列挙子により定義されています。
typedef enum {
IpDadStateInvalid = 0,
IpDadStateTentative, // 一時的利用
IpDadStateDuplicate, // 重複を検知
IpDadStateDeprecated, // 有効期限切れ
IpDadStatePreferred, // 有効なIPv6アドレス
} IP_DAD_STATE;
エニーキャストアドレスとマルチキャストアドレスのリストを格納するFirstAnycastAddressメンバおよびFirstMulticastAddressメンバ
IP_ADAPTER_ANYCAST_ADDRESS構造体は、ユニキャストアドレスを格納するFirstUnicastAddressメンバとリストを構成して複数のアドレスを格納する点について同一です。しかしプロトコルスタック上でエニーキャストアドレスとマルチキャストアドレスの寿命は設定できないため、これらの構造体はメンバが大幅に簡素化されてアドレス情報のみが保持されています。
typedef struct _IP_ADAPTER_ANYCAST_ADDRESS {
union {
ULONGLONG Alignment;
struct {
ULONG Length;
DWORD Flags;
};
};
struct _IP_ADAPTER_ANYCAST_ADDRESS *Next;
SOCKET_ADDRESS Address;
} IP_ADAPTER_ANYCAST_ADDRESS, *PIP_ADAPTER_ANYCAST_ADDRESS;
typedef struct _IP_ADAPTER_MULTICAST_ADDRESS {
union {
ULONGLONG Alignment;
struct {
ULONG Length;
DWORD Flags;
};
};
struct _IP_ADAPTER_MULTICAST_ADDRESS *Next;
SOCKET_ADDRESS Address;
} IP_ADAPTER_MULTICAST_ADDRESS, *PIP_ADAPTER_MULTICAST_ADDRESS;
DNSに関する情報を格納するFirstDnsServerAddress, DnsSuffixメンバ
DNSに関する情報としてDNSサーバのIPアドレス、およびドメイン名があります。それぞれIP_ADAPTER_DNS_SERVER_ADDRESS構造体のFirstDnsServerAddress変数とDnsSuffix変数(PWCHAR型)で表現されます。前者のDNSサーバのアドレスの表現形式はエニーキャストアドレスやマルチキャストアドレスと同様に、複数に保持する場合はIP_ADAPTER_DNS_SERVER_ADDRESS構造体のリストが構成されます。内部の構造についても同様です。
typedef struct _IP_ADAPTER_DNS_SERVER_ADDRESS {
union {
ULONGLONG Alignment;
struct {
ULONG Length;
DWORD Reserved;
};
};
struct _IP_ADAPTER_DNS_SERVER_ADDRESS *Next;
SOCKET_ADDRESS Address;
} IP_ADAPTER_DNS_SERVER_ADDRESS, *PIP_ADAPTER_DNS_SERVER_ADDRESS;
また後者のDnsSuffixにはドメイン名が格納されます。注意点はPWCHAR型の変数であり文字列のエンコードはワイド文字(ユニコード)により格納されることです。FriendlyNameメンバと同様に必要に応じてマルチバイト文字に変換することが必要となるかもしれません。
アダプタのタイプを示すIfTypeメンバ
アダプタのメディア種別を示す変数です。Ethernet、トンネルアダプタ、ダイヤルアップアダプタ、またループバックアダプタのような種別が存在します。詳細はヘッダファイル「ipifcons.h」にて、IF_TYPE_* から始まる定義があります。主要な定義として以下のような番号が付与されています。
#define IF_TYPE_ETHERNET_CSMACD 6 #define IF_TYPE_PPP 23 #define IF_TYPE_SOFTWARE_LOOPBACK 24 #define IF_TYPE_TUNNEL 131 // Encapsulation interface
アダプタの物理アドレスを格納するPhysicalAddress, PhysicalAddressLengthメンバ
BYTE PhysicalAddress[MAX_ADAPTER_ADDRESS_LENGTH]; DWORD PhysicalAddressLength;
アダプタが持つ物理アドレスを格納するメンバです。例えばEthernetアダプタには6バイトのMACアドレスが付与されています。Ethernet以外のメディアでは必ずしも6バイトではないことがあるため、最大で、MAX_ADAPTER_ADDRESS_LENGTHバイト(8バイト)の大きさが確保されており、実際の物理アドレスの長さはPhysicalAddressLength変数に書き込まれます。トンネルアダプタなどソフトウェア的に構成されたアダプタには擬似的に生成された物理アドレスが構成され、「6to4 Pseudo-Interface」や「TeredoTunneling Pseudo-Interface」などのトンネルタイプの仮想アダプタでは4バイトの長さになっています。なおループバックアダプタは物理アドレスを持ちません。この場合はPhysicalAddressLengthが0となります。
アダプタのMTU値を格納するMtuメンバ
対象のアダプタのMTU値を格納します。Ethernetならば通常、Mtu変数の値は1500です。ユーザによってMTU値が変更されている場合はその値が格納されます。
アダプタの状態を格納するOperStatusメンバOperStatusメンバはIF_OPER_STATUS型の変数で、up/downなどのアダプタの状態を示します。IF_OPER_STATUS型の変数は下記の列挙子によって定義されています。
typedef enum {
IfOperStatusUp = 1, // アダプタがup状態である
IfOperStatusDown, // アダプタがdown状態である
IfOperStatusTesting, // アダプタがリンクを検査中である
IfOperStatusUnknown, // アダプタの状態が不明である
IfOperStatusDormant, // アダプタの状態が休止中である
IfOperStatusNotPresent, // アダプタが存在しない
IfOperStatusLowerLayerDown // リンクレイヤ以下がdownしている
} IF_OPER_STATUS;
アダプタ(インターフェイス)インデックスを示すIpv6IfIndexメンバ
Ipv6IfIndexメンバはIPv6のみについて有効な情報です。アダプタ(インターフェイス)インデックスを表す数値です。例えばリンクローカルアドレスを記述する場合
fe80::1%6
のように%を使ってアダプタ(インターフェイス)を指定しますが、このときに指定した数字がIpv6IfIndexの値と合致します。
アダプタの属性を表現するFlagsメンバ
該当のアダプタの属性が格納されます。設定される属性値は表3に示す値の理論和となります。
表3:Flagsメンバに返却されるアダプタの属性値
|
ゾーン情報を格納するZoneIndicesメンバ
DWORD ZoneIndices[16];
ZoneIndicesメンバは16要素のDWORD配列からなっており、ゾーンを表すSCOPE_LEVEL型の列挙子を配列の添え字部分に指定します。SCOPE_LEVEL型の変数は下記のいずれかの値をとります。
typedef enum
{
ScopeLevelInterface = 1,
ScopeLevelLink = 2,
ScopeLevelSubnet = 3,
ScopeLevelAdmin = 4,
ScopeLevelSite = 5,
ScopeLevelOrganization = 8,
ScopeLevelGlobal = 14
} SCOPE_LEVEL;
例えば該当のインターフェイスがサイトローカルアドレスを使用する場合、ScopeLevelSiteを添え字としてZoneIndices[]配列を参照すると
ZoneIndices[ScopeLevelSite]
ゾーンを示す整数値を取得することができます。「netsh interface ipv6show interface」もしくは「ipv6 if」コマンドの出力(図1)を見る限り、リンクスコープ(リンクローカルアドレスのスコープ)とサイトスコープ(サイトローカルアドレス(※注2 [0])のスコープ)、およびグローバルスコープ(グローバルアドレスのスコープ)だけが使用されているようです。
図1:netsh interface ipv6 show interface "ローカル エリア接続" の出力例接続名 : ローカル エリア接続
GUID : {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
状態 : Disconnected
.
.
ファイアウォール : disabled
サイト プレフィックスの長さ : 48 ビット
ゾーン ID - Link : 6 ← リンクローカルアドレスのスコープ
ゾーン ID - Site : 1 ← サイトローカルアドレスのスコープ
リンクローカルアドレスのスコープはアダプタに依存するため、ゾーン番号(ZoneIndices[ScopeLevelLink])はアダプタ(インターフェイス)インデックスであるIpv6IfIndexメンバの値と一致します。またサイトローカルアドレスに関するゾーン番号(ZoneIndices[ScopeLevelSite])は「netsh interfaceipv6 set interface "アダプタ名" siteid="ゾーン番号"」コマンドによユーザが指定したゾーン番号と一致します。またグローバルアドレスのゾーン番号(ZoneIndices[ScopeLevelGlobal])はすべてのアダプタで同一の値を取ります。
アダプタの持つプレフィックスを格納するFirstPrefixメンバ
アダプタの持つプレフィックス情報を格納します。このメンバの情報を得るためにはGetAdaptersAddresses()関数を呼び出す際に第2引数のFlagsにGAA_FLAG_INCLUDE_PREFIXを指定しておく必要があります。このフラグの指定がないとFirstPrefixメンバにはNULLが設定されます。FirstPrefixメンバに得られる情報はアダプタの持つプレフィックス(下位64ビットはゼロクリアされます)です。なおFirstUnicastAddressメンバとの違いは、FirstUnicastAddressメンバはユニキャストアドレスを格納するもので、例えばアダプタに同一のプレフィックスの2つのIPv6ユニキャストアドレス
fe80::1 fe80::2012:34ff:fe56:789a
が付与されている場合、FirstUnicastAddressは二つ分のアドレスのリストを構成しますが、FirstPrefixメンバはプレフィックスfe80::/64を表す要素を1つだけ構成します。またIPv4アドレスの場合もホスト部がゼロクリアされたネットワークアドレスは得られますが、ネットマスクを得るためのメンバが見当たりません。なお旧来のGetAdaptersInfo()API(※注3 [0])を使用すればIPv4アドレスに関する完全な情報を得ることは可能です。
【その2へ続く [0]】
※注1 other stateful configuration機構はOフラグと呼ばれる特定のフラグが付与されたRAを受信すると、IPv6アドレスはRAに含まれるprefix informationオプションを元に構成、それ以外のDNSなどの情報は、DHCPv6などのstatefulな手法により取得する仕組みのことです。なおRAに埋め込まれるOフラグの定義や、Oフラグの利用方法は RFC2461とdraft-ietf-ipv6-ra-mo-flags-01.txtで規定されています。
※注2:サイトローカルアドレスの利用はすでに廃止が決定されており、新規に使用すべきではありません(RFC3879)。Windows XP SP2には実装されたままのため設定項目が残されています。
※注3:GetAdaptersInfo()関数もアダプタに関する情報を得るAPIです。ただしIPv4アドレスに関する情報しか取得できません。そのためIPv6にも対応するGetAdaptersAddresses()関数が増設されました。