Minecraft Java 版の LAN 接続が Connection timed out する時にやるといいかもしれないこと

Minecraft Java 版の LAN 接続が Connection timed out する時にやるといいかもしれないこと

2026年8月22日 · 20 分で読める
Background image generated by Antigravity
post 雑記
目次

TL;DR

Windows の Minecraft Java 版で LAN に公開したワールドに、別マシンから接続出来ない ( サーバーリストには表示されるのに connection timed out after 30000 ms になる )場合、 Windows ファイアウォールに javaw.exe に対する ブロックルール が自動生成されている可能性があります。

まず確認します ( 標準ユーザーのままで実行出来ます ):

Get-NetFirewallApplicationFilter -Program "*javaw.exe" | Get-NetFirewallRule |
  Select-Object DisplayName, Direction, Action, Profile, Enabled | Format-Table -AutoSize

ActionBlock のルールがあれば、それが原因です。 ブロックルールは許可ルールより優先される ため、後から許可ルールを追加しても効きません。 削除してから許可ルールを追加します ( こちらは 管理者権限が必要 ):

# ブロックルールを削除
Get-NetFirewallApplicationFilter -Program "*javaw.exe" | Get-NetFirewallRule |
  Where-Object { $_.Action -eq 'Block' } | Remove-NetFirewallRule

# 許可ルールを追加
$p = (Get-Process javaw).Path
New-NetFirewallRule -DisplayName "Minecraft Java LAN (javaw)" -Direction Inbound -Action Allow -Protocol TCP -Program $p -Profile Private

背景

息子が Windows で Minecraft Java 版のワールドを LAN に公開したので、 いつものように私も Linux 機から参加しようとしました。

ところが最近、Minecraft を最新版に更新したところ、マルチプレイのサーバーリストにはちゃんと表示されるのに、 いざ接続しようとすると connection timed out after 30000 ms で弾かれてしまうようになりました。

「Minecraft LAN connection timed out」などで検索すると、 「ファイアウォールが原因かも」というところまでは出てくるのですが、 ではどうやってそれを確かめるのか、確かめた結果どう直すのか が具体的に書かれている情報をぱっと見つけられませんでした。

普段 Xubuntu か macOS ばかりで Windows 事情にはとんと疎く、 時間と手間がかかりそうで対応に躊躇しており、しばらくは旧バージョンでプレイしてもらって凌いでいましたが、 いい加減対応するかと重い腰を上げて、Claude Opus に相談しながら対応しました。

なお、後から一次情報を当たったところ、今回の現象は Microsoft Learn の Windows Firewall Rules という記事にほぼそのまま書かれている挙動だったようです…。

なぜ「リストには出るのに繋がらない」のか

まずここが腑に落ちていませんでした。 「一覧に出ているのだから通信は出来ているはずでは?」と思ってしまいます。

しかし、この 2 つは 通信の向きが逆 のようです。

サーバーリストへの表示
ホスト側から LAN 全体へ、UDP マルチキャスト ( 224.0.2.60:4445 ) で「ここで公開してるよ」と 1.5 秒ごとに通知している。ホストから見て 送信 なので、インバウンドの制限とは無関係。
実際の接続
参加する側からホストへ TCP で繋ぎにいく。ホストから見て 受信。

マルチプレイヤーの仕組みについては Multiplayer – Minecraft Wiki に記載があります。

つまり、 一覧に見えていることと、接続出来ることは別 ということのようです。

とはいえ「通知は届いている=経路自体は生きている」ので、 今度はホスト側で受信が塞がれている可能性を疑う、というように追っていくとよいようです。

必要な権限について

「ブロックルールが存在する」という原因の特定までは子どものアカウントのまま到達出来ますが、 ルールの変更時には管理者権限が必要です。

標準ユーザーで変更操作をしようとすると以下のようになります:

Remove-NetFirewallRule : アクセスが拒否されました。
    + CategoryInfo          : PermissionDenied: (MSFT_NetFirewal...ystemName = ""):root/standardcimv2/MSFT_NetFirewallRule) [Remove-NetFirewallRule], CimException
    + FullyQualifiedErrorId : Windows System Error 5,Remove-NetFirewallRule

Windows System Error 5ERROR_ACCESS_DENIED のことです ( System Error Codes (0-499) )。

ERROR_ACCESS_DENIED

5 (0x5)

Access is denied.

これが出る作業は、管理者が行う必要があります。

Note

今回は息子のアカウントでログインしたまま、PowerShell を「管理者として実行」し、 表示されたダイアログに自分(管理者アカウント)の PIN を入力しました。

これは UAC ( User Account Control ) の credential prompt と呼ばれる挙動です。 標準ユーザーの既定の昇格方法について、Microsoft Learn の How User Account Control works にはこうあります。

The default, built-in UAC elevation component for standard users is the credential prompt.

「Admin Approval Mode」ではありません。 あれは管理者アカウントが consent prompt / credential prompt を 使って自分自身を昇格させる仕組みの呼び名で、標準ユーザーには適用されないそうです。 標準ユーザー側のポリシーの既定値は、 User Account Control settings and configurationBehavior of the elevation prompt for standard users の項にこう明記されています。

Prompt for credentials (default): When an operation requires elevation of privilege, the user is prompted to enter an administrative user name and password. If the user enters valid credentials, the operation continues with the applicable privilege.

PIN で通るのは Windows Hello が資格情報の入力手段として使えるためで、扱いは同じです。

切り分け

1. ping で経路を確認する

サーバーリストに表示されている IP アドレス宛に ping を打ちます ( ここでは 192.168.1.100 とします )。

ping -c3 192.168.1.100
64 bytes from 192.168.1.100: icmp_seq=1 ttl=128 time=0.388 ms
64 bytes from 192.168.1.100: icmp_seq=2 ttl=128 time=0.388 ms
64 bytes from 192.168.1.100: icmp_seq=3 ttl=128 time=0.464 ms
3 packets transmitted, 3 received, 0% packet loss

通りました。これで、Wi-Fi のプライバシーセパレータ ( AP アイソレーション ) や VLAN 分離といった 経路側の問題は除外 出来るんだそう。

Note

ping コマンド自体は普段からよく使うのですが、 「なぜ ping が通れば経路の問題を除外出来るのか」は改めて聞かれると曖昧だったので、教わった内容を残しておきます。

ping が使う ICMP は、TCP や UDP と同じく IP データグラムに載せて運ばれるプロトコルです。 RFC 792 にはこうあります。

ICMP messages are sent using the basic IP header. The first octet of the data portion of the datagram is a ICMP type field; the value of this field determines the format of the remaining data.

( RFC 792 - Internet Control Message Protocol )

つまり ICMP も TCP も、同じ IP の上に乗っているだけの兄弟プロトコルです。 AP アイソレーションや VLAN 分離のような経路レベルの分離は、一般的には IP より下のレイヤーで経路そのものを分けるものなので、 プロトコルを問わず ICMP も TCP も一緒に落ちるはず、というのが一般的な理解のようです ( 実装は製品によって異なり、手元の機材向けの一次資料は見つけられませんでした )。 逆に ICMP ( ping ) が通ったなら、少なくともこのレベルでは経路が分離されていないと判断出来ます。

2. nc で TCP の到達性を確認する

次に、公開されているポート宛に TCP で繋いでみます。 ポート番号は、ワールドを LAN に公開した際に表示されるものを使います。

nc -w 5 -vz 192.168.1.100 54321
nc: connect to 192.168.1.100 port 54321 (tcp) failed: Connection timed out

ここで返ってくるエラーの種類が重要らしい。

結果意味
succeededネットワークは問題なし。ゲーム側の別要因を疑う
Connection refusedRST が返っている。そのポートで待ち受けていない
Connection timed out応答が一切ない。 パケットが破棄されている

今回は timed out でした。

connect(2) のエラーとしては、こう定義されているらしいです ( connect(2) — Linux manual page )。

ECONNREFUSED A connect() on a stream socket found no one listening on the remote address.

ETIMEDOUT Timeout while attempting connection. The server may be too busy to accept new connections.

Windows ファイアウォールのインバウンドの既定動作は ブロック であり、 ブロックされたパケットは拒否応答を返すのではなく 破棄 ( drop ) されます。 これは New-NetFirewallRule-Action パラメータの説明にも明記されています ( New-NetFirewallRule (NetSecurity) )。

Block: Network packets that match all criteria specified in this rule are dropped by the firewall.

なので refused ではなく timed out になる、というのが今回の症状と一致します。

なお -w 5 を付けずに実行すると 2 分以上 待たされました。

これは Linux カーネルの SYN 再送 ( tcp_syn_retries ) を打ち切るまでの時間のようです。 既定値は 6 回で、カーネルのドキュメントにはこうあります ( ip-sysctl documentation )。

tcp_syn_retries - INTEGER

Number of times initial SYNs for an active TCP connection attempt will be retransmitted. Should not be higher than 127. Default value is 6, which corresponds to 67 seconds (with tcp_syn_linear_timeouts = 4) till the last retransmission with the current initial RTO of 1 second. With this the final timeout for an active TCP connection attempt will happen after 131 seconds.

指数バックオフで SYN を打ち直しつつ、最終的に約 131 秒 ( 2 分強 )で諦める、という計算のようです。 -w 5 が無いと 2 分以上 待たされたのは、これに一致します。

3. Windows 側で待ち受けを確認する

ここまでで「Windows のホスト上で受信が塞がれている」ところまで絞れたので、Windows 側に移ります。 以降のコマンドはすべて、削除・作成の 2 つを除いて標準ユーザーのまま実行出来ます。

まずゲームがちゃんと待ち受けているかを、ループバック経由で確認します。

Test-NetConnection -ComputerName 127.0.0.1 -Port 54321
ComputerName     : 127.0.0.1
RemoteAddress    : 127.0.0.1
RemotePort       : 54321
InterfaceAlias   : Loopback Pseudo-Interface 1
SourceAddress    : 127.0.0.1
TcpTestSucceeded : True

Test-NetConnection は ping ・ TCP ポート疎通・経路トレースをまとめて実施してくれるやーつらしいです ( Test-NetConnection (NetTCPIP) )。 TcpTestSucceeded : True なら、ゲームは正常に待ち受けています。 False なら、そもそも公開されていないか、ポート番号が違います。

併せて、待ち受けアドレスと javaw.exe の実パスも確認しておきます。 このパスは後で使います。

(Get-Process javaw).Path
C:\Users\<user>\AppData\Local\Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local\runtime\java-runtime-epsilon\windows-x64\java-runtime-epsilon\bin\javaw.exe
Warning

ここで Get-Process javaw | Select-Object Id, Path と書くと、パスが途中で省略されます。

   Id Path
   -- ----
15056 C:\Users\<user>\AppData\Local\Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local\runt...

Format-Table は表示幅に収まらない分を切り詰めるためです ( Using Format commands to change output view )。 -AutoSize-Wrap を付ける手もありますが、 今回のように 値がひとつ欲しいだけなら、そもそも表にしない のが確実です。 (Get-Process javaw).Path は文字列そのものを返すので、幅の制限を受けません。

ウィンドウを広げて対処することも出来ますが、後述するようにこのパスはルール作成にそのまま使うので、 変数に入れて扱うほうが安全です。

エージェントは気を効かせてしれっとテーブル出力にしてきたりするので要注意。

待ち受けアドレスも見ておきます。

Get-NetTCPConnection -LocalPort 54321 -State Listen
LocalAddress  LocalPort RemoteAddress RemotePort State
------------  --------- ------------- ---------- -----
::            54321     ::            0          Listen

Get-NetTCPConnection は現在の TCP コネクションをローカル/リモートアドレスや状態付きで一覧してくれます ( Get-NetTCPConnection (NetTCPIP) )。 LocalAddress0.0.0.0 または :: なら全インターフェイスで待ち受けているので正常です。

:: ( IPv6 のワイルドカード )しか出てこないので、 Claude は IPv4 で待ち受けていない可能性を疑ったらしく、 色々と説明を補強してくれました。

直前の Test-NetConnection 127.0.0.1 は IPv4 で成功しているし、最終的に Linux 側から IPv4 アドレスで接続出来ているので、 少なくとも今回の事象の原因ではないだろう、という判断になったようです。

そして、これは デュアルスタックソケット の挙動、という結論になりました。 Winsock のドキュメントにはこうあります ( Dual-Stack Sockets for IPv6 Winsock Applications )。

By default, an IPv6 socket created on Windows Vista and later only operates over the IPv6 protocol. In order to make an IPv6 socket into a dual-stack socket, the setsockopt function must be called with the IPV6_V6ONLY socket option to set this value to zero before the socket is bound to an IP address. When the IPV6_V6ONLY socket option is set to zero, a socket created for the AF_INET6 address family can be used to send and receive packets to and from an IPv6 address or an IPv4 mapped address.

つまり :: にバインドされたソケットが IPv4 も受け付けるかどうかは、 ソケット側が IPV6_V6ONLY を明示的に無効化しているかどうか次第で、Windows の既定は IPv6 専用だそうです。 今回のゲームサーバーは IPv4 からの接続を実際に受け付けているので、 Java 側でデュアルスタックとして待ち受けている、ということのようです。 なので :: だけが表示されていても、IPv4 で繋がらない理由にはなりません。

犯人は「勝手に作られていたブロックルール」

javaw.exe で有効になっているファイアウォールルールの一覧を確認します:

Get-NetFirewallApplicationFilter -Program "*javaw.exe" | Get-NetFirewallRule |
  Select-Object DisplayName, Direction, Action, Profile, Enabled | Format-Table -AutoSize
DisplayName Direction Action Profile Enabled
----------- --------- ------ ------- -------
javaw.exe     Inbound  Allow Private    True
javaw.exe     Inbound  Allow Private    True
javaw.exe     Inbound  Allow Private    True
javaw.exe     Inbound  Allow Private    True
javaw.exe     Inbound  Allow Private    True
javaw.exe     Inbound  Allow Private    True
javaw.exe     Inbound  Allow Private    True
javaw.exe     Inbound  Allow Private    True
javaw.exe     Inbound  Block Private    True
javaw.exe     Inbound  Allow Private    True
javaw.exe     Inbound  Allow Private    True

ActionBlock のルールが存在していました。犯人はこれです。

なおこのワンライナーは、ルールそのものではなく フィルタオブジェクト を経由しているのがポイントだそうで、 Windows ファイアウォールでは、ルールの ProgramPackage といった条件は NetFirewallApplicationFilter という別オブジェクトとして保持されており、 これらの条件でルールを検索するにはフィルタオブジェクト側から辿る必要があるんだそう ( Get-NetFirewallApplicationFilter (NetSecurity) )。

Get-NetFirewallRule に「このプログラムのルールを出して」と直接聞けないのは、そういう構造だからでした。

PowerShell 難しい!

許可ルールが 10 件も溜まっていた

もうひとつ気になったのが、 Allow のルールがやたら多いことです。 表示名はどれも javaw.exe で区別が付きませんが、 ルールごとの Program を並べてみると理由が分かります。

Get-NetFirewallApplicationFilter -Program "*javaw.exe" | Get-NetFirewallRule |
  ForEach-Object { [PSCustomObject]@{
    Action   = $_.Action
    Protocol = ($_ | Get-NetFirewallPortFilter).Protocol
    Runtime  = (($_ | Get-NetFirewallApplicationFilter).Program -split '\\')[-3]
  }} | Sort-Object Runtime, Protocol | Format-Table -AutoSize

手元では jre-legacyjava-runtime-alphabetagammadelta の 5 世代分のパスに対して、それぞれ 2 件ずつルールが存在していました ( 最新の epsilon はここには含まれません。ブロックルール側だからです )。

この「2 件ずつ」は、後述する Microsoft Learn の記述 「通常は TCP と UDP に 1 つずつ、計 2 つ作られる」 とも符合します。

ドキュメントで読んだときはピンと来ませんでしたが、こうして並ぶと納得します。

バージョンごとにランタイムが違う、というのが決定打だった

そして、この一覧から解決したことがもうひとつありました。

冒頭の 昔のバージョンなら繋がっていた ことについて、ランチャーの仕組みで説明が付きます。

Minecraft ランチャーは、起動するバージョンに必要な JRE を自動でダウンロードし、世代ごとに別ディレクトリで管理します ( Tutorial:Update Java – Minecraft Wiki )。 対応はおおよそ以下の通りです。

ランタイムJava対象バージョン
jre-legacy8〜1.16.5
java-runtime-alpha161.17 系
java-runtime-beta171.18 初期
java-runtime-gamma171.18.2 〜
java-runtime-delta211.20.5 〜
java-runtime-epsilon25最新系

Java と Minecraft バージョンの対応は Wiki のリリースノートから追えますが、 ランタイムのディレクトリ名 ( jre-legacyjava-runtime-alpha など )と Java バージョンの対応は Mojang 公式ドキュメントには見当たらず、有志のランチャープロジェクトのドキュメントで確認しました ( Using custom Java versions | Legacy Launcher Wiki )。

つまり、こういうことのようです:

  • 昔の起動構成 → jre-legacydelta のいずれかを使う → 許可ルールがある → 繋がる
  • 最新の起動構成 → epsilon を使う → ブロックルールがある → 繋がらない

ファイアウォールのルールは完全パス一致なので、ランタイムが違えば完全に別のプログラム扱いです。 「バージョンによって繋がったり繋がらなかったりする」という一見不可解な症状は、 実は ファイアウォールルールがプログラム単位である ことの、分かりやすい証拠だったわけです。

同じ症状に遭遇したら、 古いバージョンの起動構成で試してみる のは良い切り分けになりそうです。 それで繋がるなら、ネットワーク経路ではなくプログラム単位の制限を疑うべき、と判断出来ます。

Note

細かい話ですが、自動生成されたらしいルールのパスはすべて小文字に正規化されていて、 今回自分で New-NetFirewallRule で作ったルールだけが、指定した通りの大文字混じりで残っていました。

Windows のパス照合は大文字小文字を区別しないので実害はありませんが、 自動生成されたものか手で作ったものかの見分けを付けるいち手段として使えるかも。

なぜ勝手にブロックルールが作られるのか

むしろ何故今まで問題なく動いていたかの方が謎なのですが…ブロックルールの挙動については Microsoft Learn の Windows Firewall Rules という記事に明記されていました。

アプリが初めてネットワークで待ち受けようとしたとき、対応する許可ルールが存在しないと、 「このアプリの機能のいくつかが Windows Defender ファイアウォールでブロックされています」 というダイアログが表示されるらしく、原文にはこうあります。

If there’s no active application or administrator-defined allow rule(s), a dialog box prompts the user to either allow or block an application’s packets the first time the app is launched or tries to communicate in the network:

  • If the user has admin permissions, they’re prompted. If they respond No or cancel the prompt, block rules are created. Two rules are typically created, one each for TCP and UDP traffic.
  • If the user isn’t a local admin and they are prompted, block rules are created. It doesn’t matter what option is selected.

In either of these scenarios, once the rules are added, they must be deleted to generate the prompt again. If not, the traffic continues to be blocked.

つまり:

  • 管理者権限のあるユーザーが 「いいえ」を選ぶかプロンプトをキャンセルすると、ブロックルールが作成される。 通常は TCP と UDP に 1 つずつ、計 2 つ
  • ローカル管理者でないユーザーの場合は、どの選択肢を選んでもブロックルールが作成される
  • いずれの場合も、 作成されたルールを削除しない限り再びプロンプトは表示されず、通信はブロックされ続ける

息子のアカウントは標準ユーザーなので、これに従うなら 2 番目のケースにあたります。 つまり息子がダイアログで 何を押していても、ブロックルールが作られていた ことになります。 「よく分からずキャンセルしてしまった」のではなく、そもそも選択の余地が無かったわけです。

これは知らないと理不尽に感じますが、 「標準ユーザーが勝手にファイアウォールに穴を開けられては困る」と考えれば、妥当な設計ではあります。

Warning

前述の通り、今まで問題無かった理由は不明です。

標準ユーザーの操作からはブロックルールしか生成されないはずなのに、 jre-legacydelta の 10 件は すべて許可ルール です。 しかもパスの構造は、前述の (Get-Process javaw).Path で見えた C:\Users\<user>\AppData\Local\Packages\...\runtime\<ランタイム名>\...\javaw.exe と同じで、 息子のユーザーディレクトリ配下と考えられます ( ファイアウォールの Profile 列= Private/Public/Domain の話ではなく、 Windows のユーザーディレクトリの話です )。

これらがどういう経緯で作られたのかは謎のままです。

ルールの作成は、作成者の SID 付きでイベントログに記録されるらしく ( ログ名 Microsoft-Windows-Windows Firewall With Advanced Security/Firewall )、 ルール追加のイベント ID は環境により 2004 または 2097 らしいです ( EventID 2004: Firewall Rule Added | Windows Forensic Handbook )。

これを元に確認してみました。

ログ自体は有効で 1000 件以上残っていましたが、 javaw.exe 関連のイベントを Where-Object で絞り込むと出てきたのは 今回の作業で自分が作ったルールの分だけ で、 jre-legacydelta の作成当時のものはすでにローテーションで消えていました。 残念。

ブロックルールは許可ルールより優先される

要注意なのは、「許可されてないなら許可すればいいだろう」と後から許可ルールを足しても効かない、という点です。

前述の Windows Firewall Rules には、「Rule precedence for inbound and outbound rules」という セクションがあり、そこにルールの優先順位が明記されています:

  1. Explicitly defined allow rules take precedence over the default block setting.
  2. Explicit block rules take precedence over any conflicting allow rules.
  3. More specific rules take precedence over less specific rules, except if there are explicit block rules as mentioned in 2. For example, if the parameters of rule 1 include an IP address range, while the parameters of rule 2 include a single IP host address, rule 2 takes precedence.

日本語にするとこうです:

  1. 明示的に定義された許可ルールは、既定のブロック設定より優先される
  2. 明示的なブロックルールは、競合する許可ルールより優先される
  3. より限定的なルールがより広範なルールより優先される ( ただし 2 のブロックルールがある場合を除く )

また、同じセクションにはこうも書かれています。

Windows Firewall doesn’t support weighted, administrator-assigned rule ordering.

Windows ファイアウォールには管理者が重み付けで順序を指定する仕組みは無い、ということです。 順番を工夫して回避することは出来ないわけです。

なので、先にブロックルールを削除する必要があります。 ここから管理者権限が必要 です。

Get-NetFirewallApplicationFilter -Program "*javaw.exe" | Get-NetFirewallRule |
  Where-Object { $_.Action -eq 'Block' } | Remove-NetFirewallRule

その上で許可ルールを追加します ( New-NetFirewallRule (NetSecurity) )。

$p = (Get-Process javaw).Path
$p  # 出力を目視確認 ( 複数出たら Minecraft のものを選ぶ。
    # 配列なら $p = $p[0] のようにインデックスで絞るか、表示された文字列をそのまま $p = "..." と入れ直す )

New-NetFirewallRule -DisplayName "Minecraft Java LAN (javaw)" -Direction Inbound -Action Allow -Protocol TCP -Program $p -Profile Private
Note

-Profile には Any も指定出来ますが、それだと Domain / Private / Public のすべてで許可することになります。 Public はカフェや駅など 信頼できないネットワーク の分類で、 Minecraft の LAN ワールドにはホワイトリストの仕組みが無いので、 そこでも javaw.exe への着信を許可してしまうのは過剰です。

Windows が自動生成する既存のルールも、前述の一覧の通りすべて Profile: Private だったので、それに粒度を合わせました。

ただし -Profile Private が効くのは、自宅のネットワークが実際に Private に分類されている場合だけです。 念のため確認しておきます。

Get-NetConnectionProfile
Name                     : Network
InterfaceAlias           : イーサネット
InterfaceIndex           : 18
NetworkCategory          : Private
DomainAuthenticationKind : None
IPv4Connectivity         : Internet
IPv6Connectivity         : Internet

NetworkCategory : Private になっていました。 Public のままだと、この許可ルールは効きません。

これで Linux 側から無事に接続出来ました! 🎉

作成後は、実際に登録された状態も確認しておきます。

Get-NetFirewallApplicationFilter -Program "*javaw.exe" | Get-NetFirewallRule |
  Select-Object DisplayName, Direction, Action, Profile, Enabled | Format-Table -AutoSize
DisplayName                Direction Action Profile Enabled
-----------                --------- ------ ------- -------
javaw.exe                    Inbound  Allow Private    True
javaw.exe                    Inbound  Allow Private    True
javaw.exe                    Inbound  Allow Private    True
javaw.exe                    Inbound  Allow Private    True
javaw.exe                    Inbound  Allow Private    True
javaw.exe                    Inbound  Allow Private    True
javaw.exe                    Inbound  Allow Private    True
javaw.exe                    Inbound  Allow Private    True
Minecraft Java LAN (javaw)   Inbound  Allow Private    True
javaw.exe                    Inbound  Allow Private    True
javaw.exe                    Inbound  Allow Private    True

Block の行が消え、追加した Minecraft Java LAN (javaw)Inbound / Allow / Private / True で登録されているのが分かります。

Note

今回見つかったブロックルールは 1 件だけでした。 前述の引用にある「通常は TCP と UDP に 1 つずつ」に照らすと 1 件少ないので、状況によって変わるようです。

Action -eq 'Block' で引っかかったものをまとめて消す今回のやり方なら、件数によらず対処出来ます。

許可ルールの方は、Minecraft Java 版のゲーム接続が TCP なので今回は TCP だけで足りました。

なお、ファイアウォールルールはマシン全体に適用されるので、 このルールは息子のアカウントでも私のアカウントでも有効です。 ただしルールが指しているのは 息子のユーザーディレクトリ配下の javaw.exe なので、 私が自分のアカウントで Minecraft を起動する場合は JRE のパスが変わり、別途ルールが必要になります。

ハマりどころ

-Program のパスに空白が混入しても、エラーにならない

最初、パスを手でコピペした際に先頭に半角スペースが紛れ込んでいました。

-Program " C:\Users\...\javaw.exe"
          ↑ これ

アプリケーションルールでは ワイルドカードが使用出来ず、完全パスでのみルールを作成出来ます。 つまり文字列としての厳密一致なので、先頭にスペースがあると実体と一致せず、 そのルールは永久に発火しません。 しかもルールの作成自体はエラーにならず、一見成功したように見えます。

作成後は次のコマンドで、登録された値を目視確認しておくと安全です。

Get-NetFirewallRule -DisplayName "Minecraft Java LAN (javaw)" |
  Get-NetFirewallApplicationFilter | Select-Object -ExpandProperty Program

前述のように $p 経由で渡してしまうのが確実です。

PowerShell の行継続文字

これも Windows に不慣れゆえに引っかかった点です。 Web で見かける PowerShell のコマンドは、しばしばバッククォートで改行されています。

New-NetFirewallRule -DisplayName "Minecraft Java LAN (javaw)" `
  -Direction Inbound -Action Allow -Protocol TCP `
  -Program $p -Profile Private

このバッククォートは行継続文字で、見やすさのために複数行に分けた結果として必要になっているだけです。 1 行にまとめるなら不要です。

そして バッククォートの直後に空白があると効きません。 行の一番最後の文字でないと駄目なので、コピペで末尾に空白が紛れ込むと壊れます。 1 行にしてしまうほうが事故が少ないと思います。

ポート単位ではなくプログラム単位でルールを作る

LAN 公開のポート番号は公開するたびに変わります。 そのため、ポート番号を指定したルールを作ると、次回にはもう役に立ちません。 -Program でプログラム単位のルールにしておけば、ポートが変わっても効き続けます。

ランチャー更新でパスが変わる

今回の javaw.exe のパスは、Microsoft Store 版ランチャー同梱の JRE でした:

C:\Users\<user>\AppData\Local\Packages\Microsoft.4297127D64EC6_8wekyb3d8bbwe\LocalCache\Local\runtime\java-runtime-epsilon\windows-x64\java-runtime-epsilon\bin\javaw.exe

前述の通り、 java-runtime-epsilon の部分は Minecraft のバージョンによって変わります。 アプリケーションルールではワイルドカードが使えないので、 ランチャーが新しい JRE を使うようになると、以前作ったルールのパスが実体とずれて同じ症状が再発します。

許可ルールが 10 件も溜まっていたのは、これが世代ごとに繰り返された結果です。 再発したときは (Get-Process javaw).Path で新しいパスを取り直して作り直せば大丈夫です。

ファイアウォール以外を疑う場合

手順 3 まで確認してもファイアウォールが原因でなさそうなら、以下も候補になるらしいです ( 今回は実際に踏み込んで調べたわけではなく、聞きかじった話です ) 。

  • サードパーティのセキュリティソフト ( ESET / Norton / Avast 等 ) の独自ファイアウォール。Windows ファイアウォールを切っても効き続けるとか
  • Hyper-V や WSL の vEthernet 側のサブネットが混ざっている、とか
  • 有線と無線で VLAN が分かれている、とか

切り分けの補助としては、 逆向き ( Linux 側で LAN 公開してホスト側から接続 )を試すのも有効らしいです。 逆向きが通るなら経路は健全で、Windows のインバウンドが原因だと確定出来る、とのことでした。

参考

終わりに

検索で出てくる情報は「ファイアウォールが原因かもしれない」で止まっていることが多く、 そこから先の どのコマンドで何を確認して、何が分かったら何をするのか が繋がっていませんでした。

今回 Claude Opus に相談して助かったのは、 「一覧に出るのは送信方向のマルチキャストだから、インバウンドの制限とは別の話」 という説明で症状の意味が腑に落ちたことと、 timed outrefused の違いから何が言えるかを踏まえた手順を出してくれたことでした。

そして記事を書くにあたって一次情報を確認したところ、 「標準ユーザーの場合は何を押してもブロックルールが作られる」も 「ブロックルールが許可ルールより優先される」も、Microsoft Learn に明記されている仕様でした。 知らない領域だと、そもそも 何を調べればその答えに辿り着くのか が分からないものだな、と改めて思います。

同じところで詰まっている親御さんの助けになれば幸いです。

著者
ソフトウェアエンジニア
父親兼エンジニア
comments powered by Disqus