web-dev-qa-db-ja.com

単一のプロセスインスタンスから複数のネットワーク名前空間を作成する方法

次のC関数を使用して、複数のネットワーク名前空間から単一のプロセスインスタンスを作成しています:

_void create_namespace(const char *ns_name)
{
    char ns_path[100];

    snprintf(ns_path, 100, "%s/%s", "/var/run/netns", ns_name);
    close(open(ns_path, O_RDONLY|O_CREAT|O_EXCL, 0));
    unshare(CLONE_NEWNET);
    mount("/proc/self/ns/net", ns_path, "none", MS_BIND , NULL);
}
_

プロセスがすべての名前空間を作成し、tapインターフェイスを1つのネットワーク名前空間のいずれかに追加した後(_ip link set tap1 netns ns1_コマンドを使用)、次に実際には、すべての名前空間でこのインターフェイスが表示されます(おそらく、これは実際には異なる名前の単一の名前空間です)。

しかし、複数のプロセスを使用して複数の名前空間を作成すると、すべてが正常に機能します。

ここで何が間違っている可能性がありますか?単一のプロセスインスタンスからこれを機能させるには、追加のフラグをunshare()に渡す必要がありますか? 1つのプロセスインスタンスで複数のネットワーク名前空間を作成できないという制限はありますか?または、_/proc/self/ns/net_が実際に複数回マウントされているため、mount()呼び出しに問題がありますか?

更新:unshare()関数は複数のネットワーク名前空間を正しく作成しているようですが、_/var/run/netns/_のすべてのマウントポイントは、実際にはそのディレクトリにマウントされた最初のネットワーク名前空間を参照しています。

pdate2:最善のアプローチは、別のプロセスをfork()し、そこからcreate_namespace()関数を実行することです。とにかく、fork()呼び出しを含まないより良い解決策を聞いたり、少なくとも単一のプロセスから複数のネットワーク名前空間を作成および管理することが不可能であることを証明する確認を取得したりできれば幸いです。

pdate3:次のコードを使用して、unshare()で複数の名前空間を作成できます。

_int  main() {
    create_namespace("a");
    system("ip tuntap add mode tap tapa");
    system("ifconfig -a");//shows lo and tapA interface
    create_namespace("b");
    system("ip tuntap add mode tap tapb");
    system("ifconfig -a");//show lo and tapB interface, but does not show tapA. So this is second namespace created.
}
_

しかし、プロセスが終了して_ip netns exec a ifconfig -a_と_ip netns exec b ifconfig -a_を実行した後、両方のコマンドが名前空間aで突然実行されたようです。したがって、実際の問題は、名前空間への参照を格納することです(または、mount()を正しい方法で呼び出します。しかし、これが可能かどうかはわかりません)。

17
user389238

別のプロセスからこれらの名前空間にアクセスする必要がある場合、または2つのプロセス間を行き来できるようにハンドルを取得する必要がある場合にのみ、マウント_/proc/*/ns/*_をバインドする必要があります。単一のプロセスから複数の名前空間を使用する必要はありません。

  • unshare does新しい名前空間を作成します。
  • デフォルトでは、cloneとforkは新しい名前空間を作成しません。
  • プロセスに割り当てられた各種類の「現在の」名前空間が1つあります。 unshareまたはsetnsによって変更できます。名前空間のセット(デフォルト)は、子プロセスに継承されます。

Open(_/proc/N/ns/net_)を実行すると、このファイルのiノードが作成され、後続のすべてのopen()は、同じ名前空間にバインドされているファイルを返します。詳細はカーネルdentryキャッシュの深さで失われます。

また、各プロセスには_/proc/self/ns/net_ファイルエントリが1つだけあり、バインドマウントはこのprocファイルの新しいインスタンスを作成しません。それらのマウントされたファイルを開くことはまったく同じ _/proc/self/ns/net_ファイルを直接開くことと同じです(これは最初に開いたときに指していた名前空間を指し続けます)。

「_/proc/*/ns_」はこんな風に中途半端なようです。

したがって、2つの名前空間のみが必要な場合は、次のことができます。

  • 開く_/proc/1/ns/net_
  • 共有解除
  • 開く_/proc/self/ns/net_

2つを切り替えます。

2を超える場合は、clone()が必要になる場合があります。プロセスごとに複数の_/proc/N/ns/net_ファイルを作成する方法はないようです。

ただし、実行時に名前空間を切り替えたり、他のプロセスと共有したりする必要がない場合は、次のような多くの名前空間を使用できます。

  • ソケットを開き、メイン名前空間のプロセスを実行します。
  • 共有解除
  • ソケットを開き、2番目の名前空間(netlink、tcpなど)のプロセスを実行します
  • 共有解除
  • .。
  • 共有解除
  • ソケットを開き、N番目の名前空間(netlink、tcpなど)のプロセスを実行します

開いているソケットはネットワーク名前空間への参照を保持するため、ソケットが閉じられるまで収集されません。

また、netlinkを使用して、ソース名前空間でnetlinkコマンドを送信し、PIDまたは名前空間FD(後で使用しない)のいずれかでdst名前空間を指定することにより、名前空間間でインターフェイスを移動することもできます。

その名前空間に依存する_/proc_エントリにアクセスする前に、プロセスの名前空間を切り替える必要があります。 「proc」ファイルが開かれると、名前空間への参照を保持します。

11
juris

ネットワーク名前空間 は、 設計によるcloneの呼び出しで作成され、後で変更できますnsharenshareを使用して新しいネットワーク名前空間を作成した場合でも、実際には、実行中のプロセスのネットワークスタックを変更するだけであることに注意してください。 nshareは他のプロセスのネットワークスタックを変更できないため、nshareだけで別のプロセスを作成することはできません。

新しいネットワーク名前空間が機能するには、新しいネットワークスタックが必要であるため、新しいプロセスが必要です。それで全部です。

良いニュースは、clonesee で非常に軽量にできることです。

Clone() UNIXの従来のfork()システムコールとは異なり、親プロセスと子プロセスがリソースを選択的に共有または複製できます。

このネットワークスタックでのみ迂回することができます(そして、メモリスペース、ファイル記述子のテーブル、およびシグナルハンドラのテーブルを回避できます)。新しいネットワークプロセスは、実際のforkというよりもthreadのようにすることができます。

それらは、Cコード、Linuxカーネル、LXCツールを使用して操作できます。

たとえば、デバイスを新しいネットワーク名前空間に追加するには、次のように簡単です。

echo $PID > /sys/class/net/ethX/new_ns_pid

利用可能なCLIの詳細については、 このページ を参照してください。

C側では、lxc-unshareの実装を見ることができます。その名前にもかかわらず、それはcloneを使用します、あなたが 見ることができる (lxc_cloneは ここ )。 LTP実装 も見ることができます。ここで、作成者はフォークを直接使用することを選択しました。

[〜#〜] edit [〜#〜]:永続化するために使用できるトリックがありますが、一時的であってもフォークする必要があります。

ipsource2 のこのコードを見てください(明確にするためにエラーチェックを削除しました):

snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name);

/* Create the base netns directory if it doesn't exist */
mkdir(NETNS_RUN_DIR, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);

/* Create the filesystem state */
fd = open(netns_path, O_RDONLY|O_CREAT|O_EXCL, 0);
[...]
close(fd);
unshare(CLONE_NEWNET);
/* Bind the netns last so I can watch for it */
mount("/proc/self/ns/net", netns_path, "none", MS_BIND, NULL)

このコードをフォークされたプロセスで実行すると、新しいネットワーク名前空間を自由に作成できるようになります。それらを削除するには、このバインドをアンマウントして削除するだけです。

umount2(netns_path, MNT_DETACH);
if (unlink(netns_path) < 0) [...]

EDIT2:もう1つの(汚い)トリックは、単に「ip netns add ..」cliをsystemで実行することです。

18
Coren