LoginSignup
0

More than 5 years have passed since last update.

NetBSDカーネルのsysctl処理を追いかけてみた

Last updated at Posted at 2014-12-15

NetBSDカーネルのsysctl処理を追いかけてみた

今日のNetBSD Advent Calendarでは、NetBSDカーネル内部のsysctl処理を追いかけてみます。ちょうど同じ日が空いていたので、Linuxのsysctl処理も同様に追いかけてみています。よかったらそちらもご覧ください。

sysctl

sysctlはカーネルの内部状態を設定、参照するための機能です。例えば、IPパケットのフォワーディング等の設定も
sysctlで有効・無効にすることができます。NetBSDでは、以下のコマンドにより、IPパケットのフォワーディング設定を
確認できます。

kimiuso tsubaki $ sysctl net.inet.ip.forwarding 
net.inet.ip.forwarding = 0

環境について

今回はNetBSD-6.1.5-amd64の環境でソースコードを追いかけてみます。

カーネルソースコードを追いかけてみる

まずは調べるための足がかりが欲しいので、"forwarding"でソースコードをgrepしてみます。
それっぽいファイルが引っかかりますね。

kimiuso tsubaki $ cd /usr/src/sys/
kimiuso sys $ find sys/netinet -type f | grep \\.c$ | xargs grep -n \"forwarding\"
sys/netinet/ip_input.c:1749:                   CTLTYPE_INT, "forwarding",

上記はsysctl_createv()の一部ですね。何となくforwarding関連のデータ構造を構築する関数のように見えます。

/usr/src/sys/netinet/ip_input.c:
1724 static void
1725 sysctl_net_inet_ip_setup(struct sysctllog **clog)
1726 {
...
1747         sysctl_createv(clog, 0, NULL, NULL,
1748                        CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
1749                        CTLTYPE_INT, "forwarding",
1750                        SYSCTL_DESCR("Enable forwarding of INET datagrams"),
1751                        NULL, 0, &ipforwarding, 0,
1752                        CTL_NET, PF_INET, IPPROTO_IP,
1753                        IPCTL_FORWARDING, CTL_EOL);

sysctl_createv()を呼んでいるsysctl_net_inet_ip_setup()は、ip_init()から呼ばれています。

/usr/src/sys/netinet/ip_input.c:
 292 /*
 293  * IP initialization: fill in IP protocol switch table.
 294  * All protocols not implemented in kernel go to raw IP protocol handler.
 295  */
 296 void
 297 ip_init(void)
 298 {
 ...
 302         sysctl_net_inet_ip_setup(NULL);

ip_init()はstruct protosw inetsw[]内でpr_initへの関数ポインタに設定されています。
メンバ変数pr_domainで指定してるinetdomainを見ると、どうやらPF_INETに紐付くデータ構造のようです。

/usr/src/sys/netinet/in_proto.c:
214 const struct protosw inetsw[] = {
215 {       .pr_domain = &inetdomain,
216         .pr_init = ip_init,
217         .pr_output = ip_output,
218         .pr_fasttimo = ip_fasttimo,
219         .pr_slowtimo = ip_slowtimo,
220         .pr_drain = ip_drainstub,
221 },
...
467 struct domain inetdomain = {
468         .dom_family = PF_INET, .dom_name = "internet", .dom_init = NULL,

sysctl_createv()を追いかけてみます。まずは引数を把握するため、関数呼び出しと引数の意味を対応させてみます。

sysctl_createv(
    clog,   // sysctl_createv(struct sysctllog **log
    0,      // int cflags
    NULL,   // const struct sysctlnode **rnode
    NULL,   // const struct sysctlnode **cnode
    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,   // int flags
    CTLTYPE_INT,    // int type
    "forwarding",   // const char *namep
    SYSCTL_DESCR("Enable forwarding of INET datagrams"),  // const char *descr
    NULL,   // sysctlfn func
    0,      // u_quad_t qv
    &ipforwarding,  // void *newp
    0,      // size_t newlen
    CTL_NET,           // ...
    PF_INET,           // ...
    IPPROTO_IP,        // ...
    IPCTL_FORWARDING,  // ...
    CTL_EOL            // ...
);

sysctlfn funcに対応する引数はNULLになっています。てっきり値を設定する関数を用意し、関数ポインタ経由で
呼び出すのかと予想していましたが違うみたいです。

1921 int
1922 sysctl_createv(struct sysctllog **log, int cflags,
1923                const struct sysctlnode **rnode, const struct sysctlnode **cnode,
1924                int flags, int type, const char *namep, const char *descr,
1925                sysctlfn func, u_quad_t qv, void *newp, size_t newlen,
1926                ...)
1927 {
...
1931         struct sysctlnode nnode, onode, *dnode;
...
1987         if (type == CTLTYPE_NODE) {
...
1993         } else if (flags & CTLFLAG_IMMEDIATE) {
...
2007         } else {
2008                 nnode.sysctl_data = newp;
2009         }

変数ipforwardingを引数として渡しています。typeとflagsの条件に引っかからない引数の組み合わせなので、
最終的にstruct sysctlnodeのメンバ変数nnode.sysctl_dataにipfowardingのアドレスが紐付けられます。

struct sysctlnodeを見ると、sysctl_dataの実体は_sud_dataで、外部データへのポインタになっています。

sys/sysctl.h:
1267 struct sysctlnode {
1268         uint32_t sysctl_flags;          /* flags and type */
1269         int32_t sysctl_num;             /* mib number */
1270         char sysctl_name[SYSCTL_NAMELEN]; /* node name */
1271         uint32_t sysctl_ver;            /* node's version vs. rest of tree */
1272         uint32_t __rsvd;
1273         union {
1274                 struct {
1275                         uint32_t suc_csize;     /* size of child node array */
1276                         uint32_t suc_clen;      /* number of valid children */
1277                         __sysc_pad(struct sysctlnode*) _suc_child; /* array of child nodes */
1278                 } scu_child;
1279                 struct {
1280                         __sysc_pad(void*) _sud_data; /* pointer to external data */
1281                         __sysc_pad(size_t) _sud_offset; /* offset to data */
1282                 } scu_data;
1283                 int32_t scu_alias;              /* node this node refers to */
1284                 int32_t scu_idata;              /* immediate "int" data */
1285                 u_quad_t scu_qdata;             /* immediate "u_quad_t" data */
1286                 bool scu_bdata;                 /* immediate bool data */
1287         } sysctl_un;
1288         __sysc_pad(size_t) _sysctl_size;        /* size of instrumented data */
1289         __sysc_pad(sysctlfn) _sysctl_func;      /* access helper function */
1290         __sysc_pad(struct sysctlnode*) _sysctl_parent; /* parent of this node */
1291         __sysc_pad(const char *) _sysctl_desc;  /* description of node */
1292 };
...
1294 /*
1295  * padded data
1296  */
...
1298 #define sud_data        __sysc_unpad(_sud_data)
...
1305 /*
1306  * nested data (may also be padded)
1307  */
...
1311 #define sysctl_data     sysctl_un.scu_data.sud_data

sysctl_dataを設定する処理まで追いかけようと思ったのですが、時間的にキビシいので、今はこれが精一杯。
とりあえず、変数ipforwardingが紐づけられているという事まで分かったので、これに紐付くデータ更新処理の部分を
追いかければ良さそうです。

まとめ

NetBSDとLinuxでsysctlの実装を少しだけ追いかけてみました。両者の実装を比較しながら解説してみたかったのですが、
それはもう少しソースコードを調べる必要がありそうです。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0