一覧NW-16-1 · WAN・トンネル・VPN

GREトンネルとMTU

同じ内側パケットを取り出し、配送先と追加長を確認します。

説明

トンネルは内側パケットに別のヘッダーを付けて、離れた終端まで運びます。途中のネットワークは外側の宛先へ配送し、終端で内側を取り出します。

GREの試料をtcpdumpとバイト列で解析し、内側パケットがどこから始まるかを確認します。内外の経路を照合した後、追加24バイトがMTUとUDPデータ上限へ与える影響を計算します。

1外側のIPと内側のIP

gre.pcapはIPv4 over GRE over IPv4の試料です。オプションのないGREを使います。

tcpdump -n -tt -vvv内外のヘッダーを解析 -r gre.pcapGREの試料

外側は192.0.2.1→198.51.100.20、内側は10.1.0.10→10.2.0.20です。外側のプロトコル番号47がGREを示し、内側にはUDP50000→9000が入っています。配送先の二組は違う役割を持ちます。

2カプセル化の境界

gre.binは外側IPv4ヘッダーから始まるパケットです。最初の48バイトを表示します。

xxd -g 1 -l 48カプセル化の境界を表示 gre.bin外側IPv4からの試料

外側IPv4の20バイトの後に00 00 08 00という4バイトがあり、その後の45から内側IPv4が始まります。GREのflags/versionは0、Protocol Type0800はIPv4です。GREそのものには内容を暗号化する機能はありません。

3元の内側パケットと一致するか

外側20バイトとGRE4バイトを除いた部分を元の内側パケットと比較します。

python3 -c追加した部分を除いて比較 'from pathlib import Path; outer=Path("gre.bin").read_bytes(); inner=Path("inner.bin").read_bytes(); print(len(inner),len(outer),outer[24:]==inner); print(outer[9],outer[24+9])'長さ・一致・各プロトコル

内側1000バイト、外側1024バイトで、取り出した内容は一致します。外側のprotocol47と内側のprotocol17も別の欄です。この試料はカプセル化直前の内側を保存して比較しています。

4外側の終端へ届く経路

内側宛先へのoverlay経路と、トンネル終端へのunderlay経路を確認します。

cat routes.txt内側と外側の経路

10.2.0.0/24はtunnel0へ送り、外側終端198.51.100.20へはwan0経由です。外側終端まで同じトンネルへ入れる設定にすると、トンネル自身を通らないと成立しない循環になる場合があります。

5MTUから内側の上限を求める

underlayのIP MTU1500から、外側IPv4の20とGREの4を引きます。

python3 -cヘッダー追加後のMTU判定 'mtu=1500; overhead=20+4; print(mtu-overhead); print([(n,n+overhead,n+overhead<=mtu) for n in (1000,1476,1500)])'内側と外側の長さ

内側は1476バイトまでで、1500バイトをそのまま入れると外側1524バイトになります。今回の1000バイト試料は収まります。追加オプションやIPv6外側、別の暗号化方式では増える長さが変わります。

6IPとUDPのヘッダーを差し引いてデータ長の上限を求める

内側もIPv4でIPヘッダー20、UDP8とすると、アプリケーションのデータ上限を計算できます。

python3 -c内側IPとUDPヘッダーも除く 'inner_mtu=1500-20-4; udp_data=inner_mtu-20-8; print(udp_data)'データ部分に残る長さ

この条件では1448バイトです。MTU超過時の分割やICMPの扱いはDFやトンネル実装にも依存するため、ヘッダーを追加した後の長さと経路の通知を確認します。MSSの調整はTCP用で、UDP全体を同じ仕組みで直すものではありません。

まとめ

内側宛先と外側終端の役割を分け、外側へ届く経路を確保します。

トンネルの追加長を含めてMTUを確認し、カプセル化と暗号化を同一視しません。

操作履歴と解説を振り返れます。

シェル / PCAPと通信の記録

1
tcpdump -n -tt -vvv -r gre.pcap
reading from file gre.pcap, link-type EN10MB (Ethernet)
0.000000 IP (tos 0x0, ttl 64, id 1, offset 0, flags [DF], proto GRE (47), length 1024)
    192.0.2.1 > 198.51.100.20: GREv0, Flags [none], length 1004
	IP (tos 0x0, ttl 64, id 1, offset 0, flags [DF], proto UDP (17), length 1000)
    10.1.0.10.50000 > 10.2.0.20.9000: [udp sum ok] UDP, length 972
2
xxd -g 1 -l 48 gre.bin
00000000: 45 00 04 00 00 01 40 00 40 2f 4a 85 c0 00 02 01  E.....@.@/J.....
00000010: c6 33 64 14 00 00 08 00 45 00 03 e8 00 01 40 00  .3d.....E.....@.
00000020: 40 11 22 e4 0a 01 00 0a 0a 02 00 14 c3 50 23 28  @."..........P#(
3
python3 -c 'from pathlib import Path; outer=Path("gre.bin").read_bytes(); inner=Path("inner.bin").read_bytes(); print(len(inner),len(outer),outer[24:]==inner); print(outer[9],outer[24+9])'
1000 1024 True
47 17
4
cat routes.txt
overlay: 10.2.0.0/24 via tunnel0
underlay: 198.51.100.20/32 via 192.0.2.254 dev wan0
5
python3 -c 'mtu=1500; overhead=20+4; print(mtu-overhead); print([(n,n+overhead,n+overhead<=mtu) for n in (1000,1476,1500)])'
1476
[(1000, 1024, True), (1476, 1500, True), (1500, 1524, False)]
6
python3 -c 'inner_mtu=1500-20-4; udp_data=inner_mtu-20-8; print(udp_data)'
1448

演習完了