✒
NAT, NAPTの仕組みを理解
本記事の目的
- 普段は実際に何が起きているのかを意識せずに使用しているNAT, NAPTの仕組みを理解する
- NAT, NAPTでアドレス変換を発生させるだけでなく、kernel内でセッションの管理がどこでどのように行われているのかも確認する
前提知識
- NAT(Network Address Translation):IPアドレスを書き換えることで通信先を変換する仕組みである。主にプライベートネットワーク内のホストが外部ネットワークと通信する際に、送信元のIPアドレスをグローバルなIPアドレスに置き換えるために用いられる。このとき、どの通信に対応するパケットであるかは別途状態として管理され、その情報に基づいて戻りの通信も正しく配送される。
- NAPT(Network Address Port Translation):IPアドレスに加えてポート番号も利用することで、複数の通信を単一のIPアドレス上で同時に扱えるようにする仕組みである。同じIPアドレスを共有しながらも、ポート番号によって各通信を識別することで、多数の端末が同時に外部と通信することを可能にする。必要に応じてポート番号の変換も行われるが、本質的にはポート番号を含めた識別子によって通信を多重化している点に特徴がある。
- conntrack(connection tracking):Linuxカーネルにおいて通信の状態を追跡し、各パケットがどの通信に属するかを管理する仕組みである。LinuxカーネルのNetfilterサブシステムに実装され、nf_conntrack、nf_conntrack_tcp、nf_conntrack_udpなどのモジュールとして実装されている。NATやNAPTはconntrackによって管理された情報を参照することでパケットの変換を一貫して行っている。
実験
以下の三つの環境での実験を実施した。
- NATの動作確認
- NAPTの動作確認
- NAPTのポート番号衝突
NATの動作確認
環境構築
ローカルのマシンで簡易的に環境を構築するために、namaspaceを用いた。以下にネットワーク構成と設定用のスクリプトを示す。
#!/bin/bash
set -e
if [ "$EUID" -ne 0 ]; then
echo "Run as root: sudo $0"
exit 1
fi
# Create namespaces
ip netns add ns1
ip netns add router
ip netns add ns2
ip netns list
# Create veth pairs
# ns1 <-> router
ip link add veth1 type veth peer name veth1-r
# router <-> ns2
ip link add veth2-r type veth peer name veth2
# Assign veth interfaces to namespaces
ip link set veth1 netns ns1
ip link set veth1-r netns router
ip link set veth2-r netns router
ip link set veth2 netns ns2
# Configure IP addresses
## ns1
ip netns exec ns1 ip addr add 10.0.0.1/24 dev veth1
ip netns exec ns1 ip link set veth1 up
ip netns exec ns1 ip route add default via 10.0.0.254
## router
ip netns exec router ip addr add 10.0.0.254/24 dev veth1-r
ip netns exec router ip addr add 192.168.0.254/24 dev veth2-r
ip netns exec router ip link set veth1-r up
ip netns exec router ip link set veth2-r up
## ns2
ip netns exec ns2 ip addr add 192.168.0.1/24 dev veth2
ip netns exec ns2 ip link set veth2 up
ip netns exec ns2 ip route add default via 192.168.0.254
# Enable IP forwarding
ip netns exec router sysctl -w net.ipv4.ip_forward=1
# Configure NAT
ip netns exec router iptables -t nat -A POSTROUTING -o veth2-r -j MASQUERADE
スクリプト内において、不明だった用語や仕組みの理解をいかに示す。
- namespace、veth:namespaceは、OS視点で異なるネットワーク空間を構築するためのものである。そのため、ネットワークごとに作成するというよりも、ネットワークスタックごとにnamespaceを作成する。ざっくりとしたイメージであるが、namespaceごとに、サーバが存在するという状況であり、それをvethによって接続している。vethは作成時にvethのペアが必ず必要であり、このペアは物理的なケーブルで接続されているというイメージである。
- iptablesでの設定内容:iptablesは、netfilterサブシステムへ設定をするためのユーザ空間ツールである。-tでは、どのテーブル(処理)に対して設定をするのかを指定している。-Aではどのchainに対してルールを追加するのかを指定している(ただし、このchainはhookに紐づいている)。-jではhookしたパケットに対する処理の内容を指定している。構造的にはtable (nat) → chain (POSTROUTING) → rule (MASQUERADE)のようになっている。
結果
- pingをしてconntrackに通信状態を登録する。
ip netns exec ns1 ping 192.168.0.1
ip netns exec router conntrack -L
- conntrackの登録内容を確認すると、送信元IPアドレスが変換されて、NATされていることがわかる。
- conntrackはエントリには、original directionの情報に続いて reply direction の情報が記載される。
$ sudo ip netns exec router conntrack -L
icmp 1 28 src=10.0.0.1 dst=192.168.0.1 type=8 code=0 id=16007 src=192.168.0.1 dst=192.168.0.254 type=0 code=0 id=16007 mark=0 use=1
conntrack v1.4.9 (conntrack-tools): 1 flow entries have been shown.
NAPTの動作確認
実験環境はNATの動作確認と同様のものとした。
結果
- ncコマンドを用いてポート番号50000でlistenをしてTCP接続するようにした。
sudo ip netns exec ns2 nc -l 50000
sudo ip netns exec ns1 nc -p 40000 192.168.0.1 50000
sudo ip netns exec ns1 nc -p 40001 192.168.0.1 50000
- 5tapleで二つの通信が識別されていることがわかる
- NATとは異なり、ポート番号も用いることで複数の通信で同一のIPアドレスを共有している
- 送信元のポート番号が異なるものを使用しているため、NAPT外側でポート番号の変換は発生していない
$ sudo ip netns exec router conntrack
tcp 6 431949 ESTABLISHED src=10.0.0.1 dst=192.168.0.1 sport=40000 dport=50000 src=192.168.0.1 dst=192.168.0.254 sport=50000 dport=40000 [ASSURED] mark=0 use=1
tcp 6 431970 ESTABLISHED src=10.0.0.1 dst=192.168.0.1 sport=40001 dport=50000 src=192.168.0.1 dst=192.168.0.254 sport=50000 dport=40001 [ASSURED] mark=0 use=1
NAPTのポート番号衝突
同じ送信元・送信先ポート番号を使用することでポート番号の衝突を発生させ、NAPTにより送信元ポート番号が変換されることを確認する。
環境構築
同一のnamespaceにおいて、ポート番号も含めた同一の送信先のセッションを作成することができないため、routerの内側に異なるnamespaceを作成した。
#!/bin/bash
set -e
if [ "$EUID" -ne 0 ]; then
echo "Run as root: sudo $0"
exit 1
fi
# Create namespaces
ip netns add ns1
ip netns add ns3
ip netns add router
ip netns add ns2
ip netns list
# Create veth pairs
# ns1 <-> router
ip link add veth1 type veth peer name veth1-r
# ns3 <-> router
ip link add veth3 type veth peer name veth3-r
# router <-> ns2
ip link add veth2-r type veth peer name veth2
# Assign veth interfaces to namespaces
ip link set veth1 netns ns1
ip link set veth1-r netns router
ip link set veth3 netns ns3
ip link set veth3-r netns router
ip link set veth2-r netns router
ip link set veth2 netns ns2
# Configure IP addresses
## ns1
ip netns exec ns1 ip addr add 10.0.1.1/24 dev veth1
ip netns exec ns1 ip link set veth1 up
ip netns exec ns1 ip route add default via 10.0.1.254
## ns3
ip netns exec ns3 ip addr add 10.0.3.1/24 dev veth3
ip netns exec ns3 ip link set veth3 up
ip netns exec ns3 ip route add default via 10.0.3.254
## router
ip netns exec router ip addr add 10.0.1.254/24 dev veth1-r
ip netns exec router ip addr add 10.0.3.254/24 dev veth3-r
ip netns exec router ip addr add 192.168.0.254/24 dev veth2-r
ip netns exec router ip link set veth1-r up
ip netns exec router ip link set veth2-r up
ip netns exec router ip link set veth3-r up
## ns2
ip netns exec ns2 ip addr add 192.168.0.1/24 dev veth2
ip netns exec ns2 ip link set veth2 up
ip netns exec ns2 ip route add default via 192.168.0.254
# Enable IP forwarding
ip netns exec router sysctl -w net.ipv4.ip_forward=1
# Configure NAT
ip netns exec router iptables -t nat -A POSTROUTING -o veth2-r -j MASQUERADE
実行コマンド
sudo ip netns exec ns2 nc -l 50000
sudo ip netns exec ns1 nc -p 40000 192.168.0.1 50000
sudo ip netns exec ns3 nc -p 40000 192.168.0.1 50000
結果
ns1→ns2のエントリのdportが24940になっている。これはNAPTによる変換後に二つの通信で5tapleが同一のものになることによって、replyパケット送信先の識別ができなくなることを防ぐために、変換時に送信元ポート番号を40000から24940に強制的に変換しているためである。
$ sudo ip netns exec router conntrack -L
tcp 6 431998 ESTABLISHED src=10.0.1.1 dst=192.168.0.1 sport=40000 dport=50000 src=192.168.0.1 dst=192.168.0.254 sport=50000 dport=24940 [ASSURED] mark=0 use=1
tcp 6 431994 ESTABLISHED src=10.0.3.1 dst=192.168.0.1 sport=40000 dport=50000 src=192.168.0.1 dst=192.168.0.254 sport=50000 dport=40000 [ASSURED] mark=0 use=1
Discussion