Net Namespaceのcopy_net_ns()の処理を読みます。カーネルのバージョンはv4.5。
まずはデータ構造で、Net Namespaceを表現するデータ構造はstruct net。定義はinlude/net/net_namespace.hにあります。この構造体にuser_namespace構造体とかns_common構造体みたいな名前空間の機能が使うデータもあれば、IPv4のnetns_ipv4構造体、パケットフィルタリングのnetns_nftables構造体などネットワーク関連のデータも諸々あります。
では、本題のcopy_net_ns()を読んでいきます。
351 struct net *copy_net_ns(unsigned long flags, 352 struct user_namespace *user_ns, struct net *old_net) 353 { 354 struct net *net; 355 int rv; 356
最初はフラグのチェックで、clone(2)などでflagにCLONE_NEWNETが設定されていなければ現在の(clone(2)なら親プロセスの)Net Namespaceの参照カウントを1つ増やして完了。
357 if (!(flags & CLONE_NEWNET)) 358 return get_net(old_net); 359
net_alloc()は名前通りな関数で、net構造体用のメモリを確保する。 細かく見るとnet_alloc_generic()という関数も呼んでいて、これはnet構造体のメンバ変数、net_generic構造体のgenのメモリも確保してます。
360 net = net_alloc(); 361 if (!net) 362 return ERR_PTR(-ENOMEM); 363
get_user_ns()はUser Namespaceの参照カウントをインクリメント。
364 get_user_ns(user_ns); 365
Net構造体を弄るためにロックを取得。
366 mutex_lock(&net_mutex);
setup_net()でnet構造体の初期化。初期化に成功したら(rv == 0)、net構造体を双方向リストの最後に追加します。
367 rv = setup_net(net, user_ns); 368 if (rv == 0) { 369 rtnl_lock(); 370 list_add_tail_rcu(&net->list, &net_namespace_list); 371 rtnl_unlock(); 372 }
net_namespace_listはnet/core/net_namespace.cで宣言されています。
32 LIST_HEAD(net_namespace_list); 33 EXPORT_SYMBOL_GPL(net_namespace_list);
最後にロックを解放して、初期化に失敗していたらUser Namespaceの参照カウントを減らし、作ったnet構造体を破棄してエラーを返して終了です。
373 mutex_unlock(&net_mutex); 374 if (rv < 0) { 375 put_user_ns(user_ns); 376 net_drop_ns(net); 377 return ERR_PTR(rv); 378 }
初期化に成功していたら作成したnet構造体を返します。
379 return net; 380 }
次にsetup_net()を見ときます。といっても、関数の半分ほどはエラー処理なので全部は見ませんけども。あと、これと言った処理は特にありません。
272 static __net_init int setup_net(struct net *net, struct user_namespace *user_ns) 273 { 274 /* Must be called with net_mutex held */ 275 const struct pernet_operations *ops, *saved_ops; 276 int error = 0; 277 LIST_HEAD(net_exit_list); 278 279 atomic_set(&net->count, 1); 280 atomic_set(&net->passive, 1); 281 net->dev_base_seq = 1; 282 net->user_ns = user_ns; 283 idr_init(&net->netns_ids); 284 spin_lock_init(&net->nsid_lock); 285
処理らしい処理ってこれくらいですね。
286 list_for_each_entry(ops, &pernet_list, list) { 287 error = ops_init(ops, net); 288 if (error < 0) 289 goto out_undo; 290 } 291 out: 292 return error;
pernet_listはリストで、これもファイルの冒頭のほうで宣言されてます。
24 /* 25 * Our network namespace constructor/destructor lists 26 */ 27 28 static LIST_HEAD(pernet_list); 29 static struct list_head *first_device = &pernet_list;
リストのデータはpernet_operations構造体です。
そして、ops_init()はこのような関数です。まず最初、98行目のif文で100行目に進む場合。これはnet_assign_generic()を呼んでますが、これは最初にnet_alloc()でメモリを確保したnet_generic構造体のgenのptrメンバ変数にdataを設定します。このときにgen->ptrに設定されているデータ数が多くて、配列を拡張する必要がある場合は配列の拡張処理が入ります。
93 static int ops_init(const struct pernet_operations *ops, struct net *net) 94 { 95 int err = -ENOMEM; 96 void *data = NULL; 97 98 if (ops->id && ops->size) { 99 data = kzalloc(ops->size, GFP_KERNEL); 100 if (!data) 101 goto out; 102 103 err = net_assign_generic(net, *ops->id, data); 104 if (err) 105 goto cleanup; 106 }
次は見たままですね。pernet_operations構造体のinit()が設定されていたら、その関数を呼び出します。そして、エラーがなければ0を返して終了です。
107 err = 0; 108 if (ops->init) 109 err = ops->init(net); 110 if (!err) 111 return 0;
ここでpernet_operations構造体のinit()を設定しているところをいくつか適当に見てみます。これはIPv4のICMPのところです。net/ipv4/icmp.cでinit()とexit()を設定を設定しています。
1234 static struct pernet_operations __net_initdata icmp_sk_ops = { 1235 .init = icmp_sk_init, 1236 .exit = icmp_sk_exit, 1237 };
IPv4のTCP(net/ipv4/tcp_ipv4.c)だとinit()、exit()、exit_batch()と3個の関数を設定しています。
2410 static struct pernet_operations __net_initdata tcp_sk_ops = { 2411 .init = tcp_sk_init, 2412 .exit = tcp_sk_exit, 2413 .exit_batch = tcp_sk_exit_batch, 2414 };
とまあ、こんな感じでプロトコル等に設定されているinit()を呼んでいきます。pernet_listの宣言のコメントに「Our network namespace constructor/destructor lists」とありますし、まさしくコンストラクタ・デストラクタですね。
改訂3版 サーバ/インフラエンジニア養成読本 (Software Design plus)
- 作者: 養成読本編集部,200
- 出版社/メーカー: 技術評論社
- 発売日: 2016/03/18
- メディア: 大型本
- この商品を含むブログを見る