IPv6 を無効にするレジストリ

IPv6 を無効にするには、レジストリにて以下のエントリー(DWORD値32ビット)を作成して、

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters@DisabledComponents

値に DisabledComponents = 0xffffffff という周知なことだと思っていたのですが、
ここ最近、実は値が変わっているとのこと。まじか!?

ここに Important ということで情報があるではないか!?
DisabledComponents = 0xffffffff
ではなく
DisabledComponents = 0xff
が正しいと…
今さら言われちゃってもねぇ。困るよ。 影響範囲は、システムスタートで 5 秒の遅延が発生するとのこと。
ってことは、今まで 5 秒遅くなってたわけね。

IP アドレスの変更をチェックする

WER 記事の最中ですが、諸事情により、急きょ、割り込み別ネタです。

Windows で IPv4 アドレスがあった場合に、変更されたことを確認する方法を紹介します。
IP Helper API の NotifyAddrChange() という API を使います。

なんとも便利な API があったりするもんです。
例えば、サービスプログラムの起動方法を “自動” にしていて OS  起動時に動き出した場合を考えてみます。
OS 起動時、割り当てられた IP アドレスが実際に有効になるのは、かなり遅いタイミングになるかと思います。
なぜかというと、まず、Windows ファイアウォールが動いてからでないと、IP アドレスの割り当ては行われません。
では、Windows ファイアウォール はいつ動き出すのかというと、WMI(Windows Management Instrumentation)
サービスが動いた後です。
では、WMI はいつ動き出すのかというと…きりがないので、この辺にしておきます。

OS 起動時、まだ、IP アドレスが有効でない場合には、localhost 127.0.0.1 が割り当てられています。
このアドレスをプログラムが使っても意味ありませんので、通常は、IP アドレスが有効になるまで”待つ”ことになるはずです。
こんなシナリオで使えそうな方法を考えてみます。上記 NotifyAddrChange() のページにあるサンプルコードは一度きりの
実行なので、これを”待つ”状態にします。

この投稿の続きを読む

LAN ケーブルは繋がっていますか?(Part.2)

前回からの続きです。
今回は、IP Helper の GetIfTable() を使って LAN ケーブルの接続状態を調べてみます。

ネットを探してみると、GetIfTable() の情報はたくさんあるので、今回は、GetIfTable2() を使ってみます。
あまり大差はないのですが…。

なお、GetIfTable2() は、Windows Vista 以降で使える API です。
まずは、GetIfTable2() で取得できる MIB_IF_TABLE2 構造体の Description メンバーだけを使ってアダプター情報だけ
を取得してみます。

すると…

GetIftable2_一覧

やっぱり、こんな結果になりました。IPv6 情報も取得できているようです。

この投稿の続きを読む

LAN ケーブルは繋がっていますか?(Part.1)

Windows 2000 以降、メディア検出機能というものが実装されています。
この機能は、ネットワークアダプターに LAN ケーブルが接続されているのか、接続されていないのかを検出する…

メディア検出

つまり、タスクトレイにあるネットワークアイコンに表示されているのは、メディア検出機能?

この機能ですが、上記 KB にあるようにレジストリで OFF にすることができます。

HKLM\System\CurrentControlSet\Services\Tcpip\Parameters

ここに、DWORD で DisableDHCPMediaSense というエントリーを作成します。
値を 1 にすると、メディア検出機能を OFF に、0 にすると ON(デフォルト)にすることができます。
(設定変更後は再起動要) もちろん、上記は Vista 以降の OS でも有効な情報です。

では、メディア検出機能を無効にしてから、LAN ケーブルを抜いてみると…
タスクトレイのアイコンにはバツマークが表示され、ケーブルが繋がっていないことがわかります。
タスクトレイのネットワークアイコンは、メディア検出機能 ON/OFF の影響は受けないようです。

実際のところ、メディア検出機能の中心はネットワークアダプターにバインドされている TCP/IP プロトコルレイヤー
自体の動作になっています。メディア検出機能 が ON になっているときに、LAN ケーブルを 抜いてしまうと、
リンク状態は OFF になり、ネットワークアダプターにバインドされている TCP/IP プロトコルレイヤーがアンロードされ、
IP アドレスが 0.0.0.0 になってしまいます。
OFF の状態だと、リンク状態はそのまま、つまり、TCP/IP プロトコルレイヤーがそのままの状態になっているので
IP アドレスもそのままの状態です。

この投稿の続きを読む

kerberos 通信は UDP?TCP?

てっきり、TCP だと思っていたんですが、Windows Vista よりも前の OS(Windows XP、Windows Server 2003/R2)は
デフォルトで UDP を使い、データ長が 1465 バイトを超えると TCP に変更されるんですね。
今更ながら、初めて知りました。

まあ、いつまで XP、2003 を使うのかってことになるのですが…。
しかし、何で、こんな仕様にしたんだろうか。不思議だ。

なお、Vista 以降の OS は、すべて TCP を使っていました(ネットワークトレースで確認済み)。

動的ポートについて

以前の記事でも書いたのですが、Windows Vista 以降で、TCP/IP で使用する動的ポートの範囲が変更されています。

動的ポートは、ephemeral ports(エフェメラル ポート)という言い方もします。
これは、特定の用途で規定しておらず、一時的な通信のために、自由に使えるポートのことです。
HTTP や LDAP のようにポート番号が規定されているポートは、well-known port と呼びます。

  • Windows Vista よりも前の OS : 1024 ~ 5000
  • Windows Vista 以降の OS : 49152 ~ 65535

IANA に従うと、Vista 以降が正しい使用方法になっています。
1024 ~ 49151 のポート番号は、予約制になっており、IANA に登録が必要になります。

   ポートの種別  ポート番号の範囲
 1  well-known port  0 ~ 1023
 2  registered port  1024 ~ 49151
 3  ephemeral port  49152 ~ 65535

 

なお、IANA で割り当てられているポート番号の一覧は、以下に記載されています。

ポート番号は正しく使いましょう。

Memory Pressure Protection?

非常に気になる機能なので、忘れないように書いておきます。

このセキュリティ更新プログラムをインストールすると

という機能が追加されます。で、気になるのが、MPP 設定です。
セキュリティ関係の機能なので、あまり詳しいことは書きませんが、セキュリティ攻撃と判定された場合

  • サーバー側のパッシブソケットから、いきなり TCP RST が飛び出します
  • 対象となるソケットはランダム抽選です

です。NonPaged Pool の動きに要注意です。

そして、最も注意が必要なのは、サーバー系 OS ではデフォルトで ON になるということです。
(クライアント系 OS はデフォルト OFF)
レジストリを変更することで、OFF に変更できます。
KB974288 には、記載がありませんが、Windows 7 / 2008 R2 も含まれます。

この機能、今のところはセキュリティ更新プログラムでの提供ですが、間違いなく、次の SP では入るでしょうね。
TCP スタックのセキュティ機能というと、Syn Attack Protect もありますのでお忘れなく。

ソケットオプションで SO_EXCLUSIVEADDRUSE を使う

よく問い合わせを受けるネタなので、書いておこうと思います。
Windows Server 2003 以降は、エンハンスト ソケット セキュリティが実装されています。
ソケットオプションに SO_EXCLUSIVEADDRUSE (ソケットを排他アクセスにする)を使う際には
必ず知っておかなければならないことです。

少し具体的な話しをします。
例として、以下の簡単なサンプルコードを考えてみます。(エラー処理は省略)

int _tmain(int argc, _TCHAR* argv[])
{
    SOCKET sdf1, sdf2;
    struct sockaddr_in addrin;
    int rc;

    WSADATA wsa_data;
    WORD wVersionRequested = MAKEWORD(2, 2);
    rc = WSAStartup(wVersionRequested, (WSADATA *)&wsa_data);
    sdf1 = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    //int optval = 1;
    //setsockopt(sdf1, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&optval, sizeof(optval));
    addrin.sin_addr.s_addr = inet_addr("127.0.0.1");
    addrin.sin_port = htons(55001);
    addrin.sin_family = AF_INET;
    rc = bind(sdf1, (SOCKADDR*)&addrin, sizeof(addrin));

    sdf2 = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    addrin.sin_addr.s_addr = htonl(INADDR_ANY);
    //addrin.sin_addr.s_addr = inet_addr("127.0.0.1");
    addrin.sin_port = htons(55001);
    addrin.sin_family = AF_INET;
    rc = bind(sdf2, (SOCKADDR*)addrin, sizeof(addrin));

    printf("port no 55001 binded\n");
    printf("netstat -ao execute\n");

    WSACleanup();
    getchar();

    return 0;
}

上記のURL 「Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE」内で「using SO_EXCLUSIVEADDRUSE」に
ある表を見てください。
エンハンスト ソケット セキュリティ以前の動作、つまり、XP 以前はどうなるかというと
First bind call ですが、sfd1 のソケットオプションは無いので、Default です。
sfd1は、ループバックアドレス inet_addr("127.0.0.1") で bind() しているので、Specific です。

次に、Second bind call ですが、sfd2 のソケットオプションは無いので、Default です。
sfd2 は、ワイルドカードアドレス htonl(INADDR_ANY) で bind() しているので、Wildcard です。

この組み合わせを見ると、INUSE (WSAEADDRINUSE:10048)になります。

次にエンハンスト ソケット セキュリティ以降の動作、2003 以降を見ます。
「Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE」の「Enhanced Socket Security」にある表を見てください。

同じ見方をすると、default で

First bind call => Specific
Second bind call => Wildcard

の組み合わせは、Success です。つまり、bind() は成功すると言っています。

SO_EXCLUSIVEADDRUSE オプションを使う場合にも、これと同じような見方をしてください。

ソケットオプションで SO_EXCLUSIVEADDRUSE を設定する場合には

  • ソケットオプションの見直し
    排他ソケットとしてTCP シーケンスを見直す
  • 使用する IP アドレスの見直し
    bind() するアドレスは再利用するのか/しないのか、また、bind() するタイミングに問題は無いか

をきちんと行わないと、bind() に際に WSAEADDRINUSE や WSAEACCES が発生します。
SO_EXCLUSIVEADDRUSE の指定は使用中のポートとの重複バインド抑止を目的にするものですが、
その際、注意しないと bind() で WSAEACCES が発生します。
この原因の多くは、TCP のシーケンス上で、まだ生きているポートに対して bind() しているだけです。
つまり、bind() のアドレス指定や TCP シーケンスの終了待ち前に処理をしてしまったことにあります。

ソケットは、closesocket() しても、すぐに完全にソケットクローズするわけではありません。
TCP のシーケンスに従い、FIN_WAIT1、FIN_WAIT2、LAST_ACK を経由したのちにクローズします。

つまり、SO_EXCLUSIVEADDRUSE オプションがついているソケットに対して、コード上で closesocket() した後、
すぐに socket()、bind() しても、まだ、完全に閉じられていない場合があり、そのポートに対して bind() すると
WSAEACCES が発生します。

以下が基本的な考え方です。

  • 同一マシン内の同じポートに対してバインドするときには、バインドアドレスを合わせ、ソケットオプションで動作を決める
    => つまり、異なるバインドアドレスを使わない
  • 対象はあくまでもポートということを忘れない。
    => 同じプロセス内だろうと、別プロセス間だろうと考え方は同じ

この辺を考慮したソケットプログラミングを行うとよいと思います。

なお、Windows Vista 以降では、エフェメラルポート の番号も変更されていますので注意してください。

Windows Vista より前の OS : 1025 ~ 5000
Windows Vista 以降の OS : 49152 ~ 65535

1024 ~ 49151 は IANA に登録が必要になります。