20

次の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);
}

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

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

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

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

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

Update3: 次のコードを使用して、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()を正しい方法で呼び出します。しかし、これが可能かどうかはわかりません)。

4

2 に答える 2

20

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

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

良いニュースは、クローンを使用して非常に軽量にすることができるということです。以下を参照してください。

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

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

それらはCコードまたはLinuxカーネルやLXCツールで操作できます。

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

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

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

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

編集:それらを永続化するために使用できるトリックがありますが、一時的であっても、フォークする必要があります。

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:別の(汚い)トリックは、システムで「ip netnsadd..」cliを実行することです。

于 2012-05-30T09:50:27.877 に答える
13

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

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

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

また、各プロセスには1つの/proc/self/ns/netファイルエントリしかなく、bindmountはこの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」ファイルが開かれると、名前空間への参照を保持します。

于 2012-06-04T13:10:29.120 に答える