読者です 読者をやめる 読者になる 読者になる

Linux net namespace: pernet_deviceめも

linux kernel

Linuxのnet namespaceにpernet_listというpernet_operation構造体のリストがあって、これがどのように設定されてるのかのめもです。 読んでるカーネルのバージョンは4.1です。

pernet_listを使ってるところは以前書いたこちらの記事にあります。 kernhack.hatenablog.com

で、このリストに要素を設定するのはどこかというとregister_pernet_device()で、kvmで動かしているarch linux(カーネルlinux 4.6.0-rc3)で確認したところ、この関数が呼ばれるのはブートのタイミングでは3回でした。カーネルのinit処理のタイミングなのでWARN_ON()を入れて確認してます。呼ばれたのは3回で、呼ばれ方としては2パターンでした。

最初と2回目はnet_dev_init()から呼ばれてます。

Apr 17 20:49:08 kerntest kernel:  [<ffffffff812e90f1>] dump_stack+0x63/0x82
Apr 17 20:49:08 kerntest kernel:  [<ffffffff8107a65b>] __warn+0xcb/0xf0
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81b5f357>] ? netdev_boot_setup+0xef/0xef
Apr 17 20:49:08 kerntest kernel:  [<ffffffff8107a78d>] warn_slowpath_null+0x1d/0x20
Apr 17 20:49:08 kerntest kernel:  [<ffffffff814aa7ad>] register_pernet_device+0x6d/0x80
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81b5f4b4>] net_dev_init+0x15d/0x1b1
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81002123>] do_one_initcall+0xb3/0x200
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81099055>] ? parse_args+0x295/0x4b0
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81b0c194>] kernel_init_freeable+0x176/0x215
Apr 17 20:49:08 kerntest kernel:  [<ffffffff815b038e>] kernel_init+0xe/0x100
Apr 17 20:49:08 kerntest kernel:  [<ffffffff815bda42>] ret_from_fork+0x22/0x40
Apr 17 20:49:08 kerntest kernel:  [<ffffffff815b0380>] ? rest_init+0x90/0x90
Apr 17 20:49:08 kerntest kernel:  [<ffffffff812e90f1>] dump_stack+0x63/0x82
Apr 17 20:49:08 kerntest kernel:  [<ffffffff8107a65b>] __warn+0xcb/0xf0
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81b5f357>] ? netdev_boot_setup+0xef/0xef
Apr 17 20:49:08 kerntest kernel:  [<ffffffff8107a78d>] warn_slowpath_null+0x1d/0x20
Apr 17 20:49:08 kerntest kernel:  [<ffffffff814aa7ad>] register_pernet_device+0x6d/0x80
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81b5f4c8>] net_dev_init+0x171/0x1b1
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81002123>] do_one_initcall+0xb3/0x200
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81099055>] ? parse_args+0x295/0x4b0
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81b0c194>] kernel_init_freeable+0x176/0x215
Apr 17 20:49:08 kerntest kernel:  [<ffffffff815b038e>] kernel_init+0xe/0x100
Apr 17 20:49:08 kerntest kernel:  [<ffffffff815bda42>] ret_from_fork+0x22/0x40
Apr 17 20:49:08 kerntest kernel:  [<ffffffff815b0380>] ? rest_init+0x90/0x90

3回目はwifiのcfg80211モジュールの初期化っぽいです。

Apr 17 20:49:08 kerntest kernel:  [<ffffffff812e90f1>] dump_stack+0x63/0x82
Apr 17 20:49:08 kerntest kernel:  [<ffffffff8107a65b>] __warn+0xcb/0xf0
Apr 17 20:49:08 kerntest kernel:  [<ffffffffa0579000>] ? 0xffffffffa0579000
Apr 17 20:49:08 kerntest kernel:  [<ffffffff8107a78d>] warn_slowpath_null+0x1d/0x20
Apr 17 20:49:08 kerntest kernel:  [<ffffffff814aa7ad>] register_pernet_device+0x6d/0x80
Apr 17 20:49:08 kerntest kernel:  [<ffffffffa0579011>] cfg80211_init+0x11/0xd6 [cfg80211]
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81002123>] do_one_initcall+0xb3/0x200
Apr 17 20:49:08 kerntest kernel:  [<ffffffff811d642f>] ? kmem_cache_alloc_trace+0x1bf/0x230
Apr 17 20:49:08 kerntest kernel:  [<ffffffff8116fcd2>] do_init_module+0x5f/0x1ed
Apr 17 20:49:08 kerntest kernel:  [<ffffffff8110556c>] load_module+0x227c/0x2900
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81102560>] ? symbol_put_addr+0x50/0x50
Apr 17 20:49:08 kerntest kernel:  [<ffffffff815b373e>] ? kmemleak_alloc+0x4e/0xb0
Apr 17 20:49:08 kerntest kernel:  [<ffffffff81105d43>] SyS_init_module+0x153/0x1a0
Apr 17 20:49:08 kerntest kernel:  [<ffffffff815bd832>] entry_SYSCALL_64_fastpath+0x1a/0xa4

net_dev_init()から呼ばれるほうはこのようにループバックデバイスとデフォルトデバイスの設定をしてます。

7498         /* The loopback device is special if any other network devices
7499          * is present in a network namespace the loopback device must
7500          * be present. Since we now dynamically allocate and free the
7501          * loopback device ensure this invariant is maintained by
7502          * keeping the loopback device as the first device on the
7503          * list of network devices.  Ensuring the loopback devices
7504          * is the first device that appears and the last network device
7505          * that disappears.
7506          */
7507         if (register_pernet_device(&loopback_net_ops))
7508                 goto out;
7509 
7510         if (register_pernet_device(&default_device_ops))
7511                 goto out;

デフォルトデバイスのほうはコンストラクタはなくて終了時の処理のみのようです。

7438 static struct pernet_operations __net_initdata default_device_ops = {
7439         .exit = default_device_exit,
7440         .exit_batch = default_device_exit_batch,
7441 };

register_pernet_device()はこんな感じで、主だったことはregister_pernet_operations()がやってます。

889 int register_pernet_device(struct pernet_operations *ops)
890 {
891         int error;
892         mutex_lock(&net_mutex);
893         error = register_pernet_operations(&pernet_list, ops);
894         if (!error && (first_device == &pernet_list))
895                 first_device = &ops->list;
896         mutex_unlock(&net_mutex);
897         return error;
898 }

register_pernet_operations()はこうです。default_device_opsとかループバックデバイスもidは振ってないようなので、ops->idが設定されてないのでここは飛ばします。で、__register_pernet_operations()です。

788 static int register_pernet_operations(struct list_head *list,
789                                       struct pernet_operations *ops)
790 {
791         int error;
792 
793         if (ops->id) {
794 again:
795                 error = ida_get_new_above(&net_generic_ids, 1, ops->id);
796                 if (error < 0) {
797                         if (error == -EAGAIN) {
798                                 ida_pre_get(&net_generic_ids, GFP_KERNEL);
799                                 goto again;
800                         }
801                         return error;
802                 }
803                 max_gen_ptrs = max_t(unsigned int, max_gen_ptrs, *ops->id);
804         }
805         error = __register_pernet_operations(list, ops);
806         if (error) {
807                 rcu_barrier();
808                 if (ops->id)
809                         ida_remove(&net_generic_ids, *ops->id);
810         }
811 
812         return error;
813 }

pernet_operations構造体の登録は登録だけじゃなくて、必要に応じてinit()の実行もしています。まず最初にpernet_listの末尾に追加します。そして、init()がせて治されてops_init()がエラーを返した場合の後処理時に使用します。

730 static int __register_pernet_operations(struct list_head *list,
731                                         struct pernet_operations *ops)
732 {
733         struct net *net;
734         int error;
735         LIST_HEAD(net_exit_list);
736 
737         list_add_tail(&ops->list, list);
738         if (ops->init || (ops->id && ops->size)) {
739                 for_each_net(net) {
740                         error = ops_init(ops, net);
741                         if (error)
742                                 goto out_undo;
743                         list_add_tail(&net->exit_list, &net_exit_list);
744                 }
745         }
746         return 0;
747 
748 out_undo:
749         /* If I have an error cleanup all namespaces I initialized */
750         list_del(&ops->list);
751         ops_exit_list(ops, &net_exit_list);
752         ops_free_list(ops, &net_exit_list);
753         return error;
754 }

最後にops_init()です。先の流れだとops->id && ops->sizeは偽になります。そして、init()が設定されている場合は実行します。

 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         }
107         err = 0;
108         if (ops->init)
109                 err = ops->init(net);
110         if (!err)
111                 return 0;
112 
113 cleanup:
114         kfree(data);
115 
116 out:
117         return err;
118 }

さて、これだとnet/ipv4/tcp_ipv4.cにあるtcp4_net_opsが登録されないため、tcp4_proc_net_init()が呼ばれません。では、これらはどうやって登録されているかというと、register_pernet_subsys()を使っています。

2327 int __init tcp4_proc_init(void)
2328 {
2329         return register_pernet_subsys(&tcp4_net_ops);
2330 }

register_pernet_subsys()はこのようになっていて、register_pernet_operations()よりシンプルになってます。

843 int register_pernet_subsys(struct pernet_operations *ops)
844 {
845         int error;
846         mutex_lock(&net_mutex);
847         error =  register_pernet_operations(first_device, ops);
848         mutex_unlock(&net_mutex);
849         return error;
850 }

register_pernet_operations()に渡すリストはfirst_deviceというリストでnet/net_namespce.cの冒頭で宣言されています。初期値はpernet_listを指しています。

29 static struct list_head *first_device = &pernet_list;

実際のところ、register_pernet_subsys()を使ってるところのほうが多いみたいです。

大体arch linuxの設定に近いはずのうちのカーネルで、ops_init()でops->init()により呼ばれた関数はこれらです。

ffffffff81272410 t proc_net_ns_init
ffffffff814a9ce0 t net_ns_net_init
ffffffff815abc20 t sysctl_net_init
ffffffff814ec0c0 t netfilter_net_init
ffffffff814ec600 t nf_log_net_init
ffffffff8149c010 t sock_inuse_init_net
ffffffff814e6610 t netlink_net_init
ffffffff814c81e0 t rtnetlink_net_init
ffffffff812ecef0 t uevent_net_init
ffffffff8149c0f0 t proto_init_net
ffffffff814d4140 t dev_proc_net_init
ffffffff814d40b0 t dev_mc_net_init
ffffffff814b0040 t netdev_init
ffffffff8144ab50 t loopback_net_init
ffffffff814d5d70 t fib_rules_net_init
ffffffff814e0040 t psched_net_init
ffffffff814ea8d0 t genl_pernet_init
ffffffff815a4b80 t wext_pernet_init
ffffffff814acc90 t sysctl_core_net_init
ffffffff815279a0 t arp_net_init
ffffffff8152c720 t devinet_init_net
ffffffff815356c0 t fib_net_init
ffffffff814eeae0 t ip_rt_do_proc_init
ffffffff8154f050 t xfrm_net_init
ffffffff8154a830 t xfrm4_net_init
ffffffff814ee490 t sysctl_route_net_init
ffffffff814edc20 t rt_genid_init
ffffffff814ee440 t ipv4_inetpeer_init
ffffffff815317c0 t igmp_net_init
ffffffff81518880 t tcp_sk_init
ffffffff8151d1d0 t tcp_net_metrics_init
ffffffff81526000 t udplite4_proc_init_net
ffffffff815294e0 t icmp_sk_init
ffffffff81545860 t ipmr_net_init
ffffffff8152fb50 t inet_init_net
ffffffff8152fc40 t ipv4_mib_init_net
ffffffff8151fd60 t raw_init_net
ffffffff815171d0 t tcp4_proc_init_net
ffffffff815232f0 t udp4_proc_init_net
ffffffff8153f630 t ping_v4_proc_init_net
ffffffff815418b0 t ip_proc_init_net
ffffffff814f4280 t ipv4_frags_init_net
ffffffff81557b30 t unix_net_init
ffffffff814cf230 t diag_net_init
ffffffff81540e40 t ipv4_sysctl_init_net
ffffffff8155d500 t inet6_net_init
ffffffff81581b50 t icmpv6_sk_init
ffffffff81594fb0 t ip6mr_net_init
ffffffff815795b0 t ndisc_net_init
ffffffff81584a00 t igmp6_net_init
ffffffff8157ff50 t raw6_init_net
ffffffff8157f5c0 t udplite6_proc_init_net
ffffffff81599810 t ipv6_proc_init_net
ffffffff81564bf0 t if6_proc_net_init
ffffffff8156fd20 t ipv6_inetpeer_init
ffffffff8156fdb0 t ip6_route_net_init
ffffffff815745d0 t fib6_net_init
ffffffff81597180 t xfrm6_net_init
ffffffff815991f0 t fib6_rules_net_init
ffffffff8156fc90 t ip6_route_net_init_late
ffffffff81590f20 t ip6_flowlabel_proc_init
ffffffff8156ce50 t ip6addrlbl_net_init
ffffffff81565d30 t addrconf_init_net
ffffffff81588540 t ipv6_frags_init_net
ffffffff8158bb80 t tcpv6_net_init
ffffffff8158d1b0 t ping_v6_proc_init_net
ffffffff815924c0 t ipv6_sysctl_net_init
ffffffff8159dcc0 t packet_net_init
ffffffffa014f130 t xt_net_init  [x_tables]
ffffffffa01788a0 t ip_tables_net_init   [ip_tables]

動くメカニズムを図解&実験! Linux超入門 (My Linux・シリーズ)

動くメカニズムを図解&実験! Linux超入門 (My Linux・シリーズ)