Nintendo Switch の「NATタイプ」判定条件

Nintendo Switch には、「接続テスト」の結果表示に「NAT タイプ」という欄があり、このタイプ判定をみることで、インターネット経由でのマルチプレイに支障がない環境かどうかを確認することができます。[任天堂の公式 FAQ](https://support.nintendo.co.jp/app/answers/detail/a_id/34283) によれば、タイプ A・B は比較的良好、タイプ C・D は「最適ではない」、タイプ F は通信対戦不可、となっており、それぞれ NAT トラバーサル（NAT 越え）のしやすさに対応していると考えられます。

しかし、このタイプの判定基準は公開されておらず、ルーターの設定項目を手探りで変えてゆくしかない状況になっています。そこで本記事では、ネットワーク（ファイアウォール）の状態がどの NAT タイプに対応しているかを調査しました。また、任意の NAT タイプを再現する方法も掲載しています。

## 結論（判定基準）
通信のしやすさは、 A > B > C > D となっていました。文字通りですね。

- UDP パケットの送受信ができなければ、タイプ F。
- [Endpoint-Independent Mapping](https://www.slideshare.net/iwashi86/webrtcnat-a-talk-on-nat-behind-webrtc/29) が満たされているとき、
    - [Endpoint-Independent Filtering](https://www.slideshare.net/iwashi86/webrtcnat-a-talk-on-nat-behind-webrtc/62) または [Address-Dependent Filtering](https://www.slideshare.net/iwashi86/webrtcnat-a-talk-on-nat-behind-webrtc/65) が行われていれば、タイプ A。
    - そうでない (Address and Port-Dependent Filtering) ならば、タイプ B。
- 満たされていないとき（Address-Dependent Mapping または Address and Port-Dependent Mapping のとき）、
    - 外部ポート番号の割り当てが予測可能ならば、タイプ C。
    - そうでないならば、タイプ D。

マッピングとフィルタリングの定義は [RFC4787](https://tools.ietf.org/html/rfc4787)（[日本語訳](http://www.can.mydns.jp/~ryo/rfc/rfc4787-ja.txt)） か、文中にリンクした @iwashi86 さんのスライドを参照してください。なお、 [RFC3489](https://tools.ietf.org/html/rfc3489#section-5) による古い分類でいうと、タイプ A が "Full Cone" または "Restricted Cone" で、タイプ B が "Port Restricted Cone"、タイプ C・D が "Symmetric" に相当します。

これだけだと分かりにくいと思うので、Nintendo Switch 実機が接続テストの際に行う動作を示します。

また、 https://github.com/yokoyama10/nintendo-switch-nat-type に、それぞれの NAT タイプを再現することができるスクリプトを掲載しました。このスクリプトは、Nintendo Switch からの UDP パケットを一度受信し、任天堂のサーバーにプロキシしています。いろいろ遊んでみましょう。

## Nintendo Switch での判定の流れ
1. ホスト A の Port 33334 に UDP パケットを送信する。送るのみで、パケットは帰ってこない。
2. ホスト A の Port 10025 に UDP パケットを送信する。このとき、変換後の外部ポート番号を `a1` とする。返信パケットが送られてくる。
3. ホスト B の Port 10025 に UDP パケットを送信する。このとき、変換後の外部ポート番号を `b1` とする。返信パケットが送られてくる。
4. ホスト A の Port 50920 からポート `a1` 宛に、UDP パケットが送られてくる。

この一連の動作を2度繰り返し、2度目の外部ポート番号を、それぞれ `a2`, `b2` とします（通信ができない F 判定の場合はさらにリトライ）。なお、1. での Port 33334 への通信は、拒否やフィルタしても判定に影響を及ぼさないようです。


|        |ホスト名                    |IPアドレス                  |
|:-------|:-------------------------|:--------------------------|
|ホスト A|nncs1-lp1.n.n.srv.nintendo.net|52.199.66.160<br />13.112.35.82|
|ホスト B|nncs2-lp1.n.n.srv.nintendo.net|54.64.157.221<br />52.193.120.207|


### タイプ A・B の場合
`a1 == b1` が成り立っています。
さらに、 4. での Port 50920 からのパケットが受信できた場合はタイプ A となり、受信できなかった場合はタイプ B。

通信相手によらず外部ポート番号が同じタイプ A・B では、任天堂のサーバーがまず外部ポート番号を観測し、それを相手に通知することで、相互通信を確立できます。

### タイプ C・D の場合
`a1 != b1` が成り立っています。
さらに、 `(a2 - a1) == (b2 - b1)` のとき（ポート番号の差が予測可能なとき）タイプ C となり、そうでないときはタイプ D。（この基準は、タイプ A・B の判定には影響を与えません）

タイプ C のようにポート番号が予測可能なとき、通信相手の使用ポートを当てずっぽうで決めて通信を開始することで、相互通信を確立できる可能性があります。（とはいえ、ここで確認されているような同一ホスト間での予測可能性だけでは、あまり意味がない気がしますが...）

## iptables での設定方法
iptables による Linux ルーターで、各 NAT タイプを再現する設定方法を示します。基本的な設定方法は別文献を参照してください。

### タイプ A
- UDP ポート (1024--65535) を Nintendo Switch に転送するように設定します。

```bash
EXTIF=ppp0  # インターネットに繋がっている NIC の名前
NINTENDO_IPADDR=192.168.0.177  # Nintendo Switch のローカル IP アドレス
iptables -t nat -I PREROUTING -i ${EXTIF} -p udp --dport 1024:65535 -j DNAT --to-destination ${NINTENDO_IPADDR}
iptables -t filter -I FORWARD -i ${EXTIF} -p udp --dport 1024:65535 -j ACCEPT
```

- または、カーネルモジュール [netfilter-full-cone-nat](https://github.com/Chion82/netfilter-full-cone-nat) を使用する方法もあります。

### タイプ B
iptables で普通に設定した IP マスカレーディングは、タイプBになります。
`iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE`
スプラトゥーン2のナワバリバトルは快適にプレイ可能です。

### タイプ C
iptables での再現手段が思いつかないです...
前掲の Ruby スクリプトでは再現に対応しています。

### タイプ D
送信元ポートのマッピングをランダム化する。
`iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE --random`
スプラトゥーン2のナワバリバトルはプレイ困難です。

## 参考文献
- [WebRTCの裏側にあるNATの話 - A talk on NAT behind WebRTC](https://www.slideshare.net/iwashi86/webrtcnat-a-talk-on-nat-behind-webrtc/)
    - 文中で参照したスライド。NAT の挙動による分類についてまとめられている。
- [[24日目] NAT Traversalって知ってますか | Cerevo TechBlog](https://tech-blog.cerevo.com/adventcalendar2016/advent24/)
    - NAT トラバーサルについての概説。
- [iptables で Symmetric NAT  &#8211;  すらりん日記](https://blog.techlab-xe.net/post-5280/)
    - iptables でタイプ D にする方法。
- [flexNES](https://github.com/jbeker/flexNES): Flexible NAT Emulation System
    - 使ってみたい
- [YAMAHA ルータと Splatoon 2 - mura日記 (halfrack)](https://halfrack.hatenadiary.org/entry/20170727/1501150452)
    - YAMAHA ルーターの不思議な挙動。タイプ B と判定されたが、オンライン対戦ができなかった例。
- [スプラトゥーン2とNATの奥深い関係 | 日経クロステック（xTECH）](https://xtech.nikkei.com/atcl/nxt/column/18/00138/073000116/)
    - 下記有料記事とネタ元が同じ人。
- [スプラトゥーン2でNAT越え、パケット流量を解明 | 日経クロステック（xTECH）](https://xtech.nikkei.com/atcl/nxt/column/18/00397/080100004/)
    - 2,500円払って読んでみたけど大したことが書いてなかった。

## 調査環境
- Nintendo Switch 10.0.1 (2020-04-22 released)
- Fedora 31 (5.3.7-301.fc31.x86_64)

本記事の条件は実機の挙動から推測したものであり、まだ明らかではない条件があるかもしれません。