Home > ブログ > Network namespaceによるネットワークテスト環境の構築

ブログ

Network namespaceによるネットワークテスト環境の構築

LinuxのUDPソケットとPath MTU Discovery」の記事で簡単なネットワークの構成図を示してPath MTU Discoveryの解説を行いました。大昔ならこの手の動作確認をしようとするとPCを複数台用意する必要がありましたが、今では、Linuxホストが一台あればテストできます。

Linuxにはコンテナごとのリソースを分離するためにnamespaceという機能がありますが、この中のnetwork namespaceを使うことで、Linuxホスト上に仮想的なホストを作成してネットワークを構築することができます。例えば、LinuxのUDPソケットとPath MTU Discoveryの記事で使った図1のようなネットワーク構成は以下の手順で作成できます。

作成するネットワーク構成
図1 作成するネットワーク構成

図1のネットワークを作成するシェルスクリプト

#!/bin/bash

# create namespace
ip netns add ns1
ip netns add ns2
ip netns add ns3

# global <-> ns1 veth
ip link add veth0 type veth peer name veth0 netns ns1
ip addr add 10.0.1.1/24 dev veth0
ip netns exec ns1 ip addr add 10.0.1.10/24 dev veth0
ip link set veth0 up
ip netns exec ns1 ip link set veth0 up

# ns1 <-> ns2 veth
ip link add veth1 mtu 1280 netns ns1 type veth peer name veth1 mtu 1280 netns ns2
ip netns exec ns1 ip addr add 10.0.2.10/24 dev veth1
ip netns exec ns2 ip addr add 10.0.2.20/24 dev veth1
ip netns exec ns1 ip link set veth1 up
ip netns exec ns2 ip link set veth1 up

# ns2 <-> ns3 veth
ip link add veth2 netns ns2 type veth peer name veth2 netns ns3
ip netns exec ns2 ip addr add 10.0.3.20/24 dev veth2
ip netns exec ns3 ip addr add 10.0.3.30/24 dev veth2
ip netns exec ns2 ip link set veth2 up
ip netns exec ns3 ip link set veth2 up

# ns1,ns2ではforwardingをする
ip netns exec ns1 sysctl -w net.ipv4.ip_forward=1
ip netns exec ns2 sysctl -w net.ipv4.ip_forward=1

# routing
ip route add 10.0.2.0/24 via 10.0.1.10
ip route add 10.0.3.0/24 via 10.0.1.10
ip netns exec ns1 ip route add 10.0.3.0/24 via 10.0.2.20
ip netns exec ns2 ip route add 10.0.1.0/24 via 10.0.2.10
ip netns exec ns3 ip route add 10.0.1.0/24 via 10.0.3.20
ip netns exec ns3 ip route add 10.0.2.0/24 via 10.0.3.20

スクリプトの内容を簡単に説明します。network namespaceに関する設定はip netnsで行います。まず最初にnetwork namespaceを3つ(ns1~ns3)作成します。

ip netns add ns1
ip netns add ns2
ip netns add ns3

各namespace(nsX)がそれぞれ仮想的なホストになります(*1)。namespace内にそれぞれ独立したネットワーク機能(*2)が提供されます。

namespace内のネットワーク設定を行う場合は、"ip netns exec <namespace名> <コマンド>"のようにexecオプションでnamespaceとコマンドを指定することで行います。

namespace内のインターフェースにIPアドレスを設定する例

ip netns exec ns1 ip addr add 10.0.2.10/24 dev veth1

namespaceを作成した後は、ネットワークインターフェースの作成とIPアドレスの設定を行っています。

# ネットワークインターフェース(veth)の作成
ip link add veth0 type veth peer name veth0 netns ns1
# IPアドレスの設定(global namespaceのveth0)
ip addr add 10.0.1.1/24 dev veth0
# IPアドレスの設定(ns1 namespaceのveth0)
ip netns exec ns1 ip addr add 10.0.1.10/24 dev veth0
:
:

ここで出てくるtype vethのインターフェースは仮想Ethernetデバイスと呼ばれるものです。network namespaceで隔離したネットワーク環境からは外部とは通信できません。何らかの方法でnamespace外と接続する必要があります。そこで使われるのが、namespace間を接続する仮想Ethernetデバイス(veth)です。

vethは必ずペアで作成され、network namespace間を接続します。 man vethによる説明を借りるとpipeのような動作をするのですが、あくまでEthernetなので、POINTOPOINTではなくBROADCASTデバイスとして動作します。

各namepsaceのvethとIPアドレスの設定ができれば、後はIPパケットの中継に関する設定を行えば、各namepsace間で通信できるようになります。

ip netns exec ns1 sysctl -w net.ipv4.ip_forward=1
ip netns exec ns2 sysctl -w net.ipv4.ip_forward=1

# routing
ip route add 10.0.2.0/24 via 10.0.1.10
ip route add 10.0.3.0/24 via 10.0.1.10
:
:

これでglobal nsからのns3の10.0.3.30にpingが通るようになります。

$ ping 10.0.3.30
PING 10.0.3.30 (10.0.3.30) 56(84) bytes of data.
64 bytes from 10.0.3.30: icmp_seq=1 ttl=62 time=0.047 ms
64 bytes from 10.0.3.30: icmp_seq=2 ttl=62 time=0.054 ms

ip netns execを使えば逆にns3からglobal nsにpingすることもできます。

$ sudo ip netns exec ns3 ping 10.0.1.1
PING 10.0.1.1 (10.0.1.1) 56(84) bytes of data.
64 bytes from 10.0.1.1: icmp_seq=1 ttl=62 time=0.040 ms
64 bytes from 10.0.1.1: icmp_seq=2 ttl=62 time=0.052 ms

ns3のveth2のパケットをキャプチャしたい場合は以下のように行えます。

$ sudo ip netns exec ns3 wireshark

しつこいですが、ns3で何らかのサーバープロセスを立ち上げてテストしたければ以下のようにできます。

$ sudo ip netns exec ns3 ./server_process

ip netns execを使えば、作成したネットワークに対していろいろなことが出来るのがわかると思います。

vethの接続先の確認の仕方

namespaceを使ってvethを大量に作成すると、どのインターフェースがどこと繋がっているかわかり辛くなります。私はvethのペアは必ず同じ名前か同じprefixを持たせるようにして識別できるようにしていますが、一応、接続先の確認方法も説明しておきます。

接続先はip linkで確認することができます。 vethの場合、link-netnsに接続先のnamespaceが表示されています。 また、インターフェース名はveth0@if2のように@if<数字>という情報が付与されており、この数字は接続先nemaspaceにおけるインターフェースのifindex(*3)になります。

global namepsaceのip link

$ ip link 
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens33:  mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 00:0c:29:fd:e2:46 brd ff:ff:ff:ff:ff:ff
    altname enp2s1
3: veth0@if2:  mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether ee:82:47:1d:71:ab brd ff:ff:ff:ff:ff:ff link-netns ns1

ns1のip link

$ sudo ip netns exec ns1 ip link
1: lo:  mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: veth0@if3:  mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 6e:e3:84:cc:64:01 brd ff:ff:ff:ff:ff:ff link-netnsid 0
3: veth1@if2:  mtu 1280 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 6e:0d:d8:b7:f8:2d brd ff:ff:ff:ff:ff:ff link-netns ns2

例えば、global namespaceのveth0は"link-netns ns1"、"veth0@if2" なので、ns1のifindex2のインターフェース、つまりveth0@if3に繋がっていることがわかります。

vethのTSO/GSO

vethでは、TSO(TCP Segmentation Offload)/GSO(Generic Receive Offload)機能が有効になっているようです。TCPを使ってPMTU Discoveryの挙動を見たい場合など、これらを無効にしておいた方がいいかもしれません。

TSO/GSOの無効化

$ sudo ethtool -K veth0 generic-segmentation-offload off
$ sudo ethtool -K veth0 tcp-segmentation-offload off

やりたいことに応じて設定してみてください。

昔話

以上で、Linuxホスト一台あれば様々なネットワークを構築してテストなどができることが分かったかと思います。一方、この方法ではホストがLinuxに限られるので、FreeBSDとかも含めた構成を組みたい時はVMWareなどを使うことになります。

VMWareもネットワーク設定機能は充実しており、linked clone機能を使えば一瞬で仮想マシンを複製できるので、ネットワーク関連のテストを行うのに重宝します。

以前はこのようなテストをしようとすると、それぞれPCを用意し、OSをインストールして、Ethernetをつなげ、同僚と電源を奪い合っていたので、それを考えるととても楽になったと感じます。一方、今ではRaspberry Piのような小型コンピューターが簡単に手に入るようになったので、それらを実際に接続して何か環境を作るというのもまた楽しいですが。

[参考]

  • man namespaces
  • man network_namespaces
  • man veth
  • man ip-netns

(*1) namespaceの名前はns1とかよりもvhost1とかにした方がわかりやすかったかもしれません。

(*2) ネットワークインターフェース、IPアドレス、ルーティングテーブル、ファイアウォール等

(*3) Kernel内でネットワークインターフェースに振られている連番。1から始まる。ip linkで表示されているコロンの前の番号はifindex。ifindexもちゃんとnamespaceごとに独立して採番されるようだ。

投稿日:2023/03/15 16:15

タグ: ネットワーク

ネットワーク関連のソフトウェア開発でしたらおまかせください。
詳細はこちら

Top

アーカイブ

タグ

Server (28) 作業実績 (21) PHP (19) ネットワーク (17) プログラミング (15) OpenSSL (10) C (8) C++ (8) PHP関連更新作業 (8) EC-CUBE (7) Webアプリ (7) laravel (6) 書籍 (5) Nginx (5) Linux (5) AWS (4) Vue.js (4) JavaScript (4) 与太話 (4) Rust (3) Symfony (2) お知らせ (2) Golang (2) OSS (1) MySQL (1) デモ (1) CreateJS (1) Apache (1)