- 必要とする知識など:
- UNIX (含む Linux) 上での C 言語と SOCKET によるプログラミング経験および TCP/IP に関する知識。
socket() システムコール、四回目です。falloc() から sys_socket() に戻ったところです。
error = socreate(uap->domain, &so, uap->type, uap->protocol,
td->td_ucred, td);
socreate() は、/usr/src/sys/kern/uipc_socket.c (http://svnweb.freebsd.org/base/release/9.1.0/sys/kern/uipc_socket.c?view=markup) で以下のように定義されています。
int
socreate(int dom, struct socket **aso, int type, int proto,
struct ucred *cred, struct thread *td)
第一引数は socket() システムコールの第一引数の int domain が渡されます。第二引数 struct socket **aso は、socreate() 関数内で struct socket 用の領域を確保しそれを返却します。第三引数と第四引数は、socket() システムコールの第二、第三引数の int type と int protocolが渡されます。第五引数には前前回(http://www.soum.co.jp/misc/asou/fbsd-kernel-socket/4.html) で出てきた認証情報が渡されます。第六引数は今までにも頻繁に出てきた struct thread です。
socreate() の最初の処理は以下のようになっています。
if (proto)
prp = pffindproto(dom, proto, type);
else
prp = pffindtype(dom, type);
server.c から socket() システムコールを呼び出す際 (http://www.soum.co.jp/misc/asou/fbsd-kernel-socket/3.html)、proto には 0 を指定しているので、pffindtype() が呼ばれます。pffindtype() は、/usr/src/sys/kern/uipc_domain.c (http://svnweb.freebsd.org/base/release/9.1.0/sys/kern/uipc_domain.c?view=markup) で以下のように定義されています。
struct protosw *
pffindtype(int family, int type)
{
struct domain *dp;
struct protosw *pr;
for (dp = domains; dp; dp = dp->dom_next)
if (dp->dom_family == family)
goto found;
return (0);
found:
for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
if (pr->pr_type && pr->pr_type == type)
return (pr);
return (0);
最初の for 文で使用している domains は、uipc_domain.c 内で以下のように定義されています。
struct domain *domains;
詳細な説明は省きますが、struct domain *domains は、下図のように struct domain と struct protosw で構成されるリスト構造の先頭の struct domain のポインタを保持します。
図では AF_INET, AF_INET6 という順番になっていますが、実際の kernel 内での順番を示すものではありません。
なお、struct domain inetdomain/struct protosw inetsw, struct domain inet6domain/struct ip6protosw inet6sw は、それぞれ /usr/src/sys/netinet/in_proto.c (http://svnweb.freebsd.org/base/release/9.1.0/sys/netinet/in_proto.c?view=markup), /usr/src/sys/netinet6/in6_proto.c (http://svnweb.freebsd.org/base/release/9.1.0/sys/netinet6/in6_proto.c?view=markup) で定義されています。
- ちょっと脱線
- 本連載の 4回目 の最初のところで SOCKET(2) からの引用を示し、「domain には PF_* を指定する。」ことを示していますが、上図の inetdomain と inet6domain で dom_family に AF_* を代入しているのは SOCKET(2) の記述と矛盾しているなと思います。
pffindtype() ですが、まずは domains が保持するリストから pffindtype() の第一引数 family と一致する dom_family を探します。、一致するものが見つかればその dom_protosw が示す struct protosw の配列から、pffindtype() の第二引数type と一致する pr_type を探します。一致するものが見つかれば、その struct protosw へのポインタを返却し、見つからなかった場合には 0 (NULL) を返却して終了します。
socreate() からの呼び出しは pffindtype(PF_INET, SOCK_DGRAM) となるので、上図左側の struct protosw inetsw の 2番目の要素のポインタ &inetsw[1] が返却されます。
今回の処理で変更した変数を青、変更した値は赤で示しています。
pffindtype() から socreate() に戻ると、エラー処理の次に prison_check_af() を呼び出します。
if (prison_check_af(cred, prp->pr_domain->dom_family) != 0)
prison_check_af() は、/usr/src/sys/kern/kern_jail.c (http://svnweb.freebsd.org/base/release/9.1.0/sys/kern/kern_jail.c?view=markup) で定義されています。prison_check_af() の詳細に関しては割愛しますが、socreate() の第五引数 struct ucred *cred で prp->pr_domain->dom_family がサポートされていることを確認しています。
prison_check_af() の次は soalloc() の呼び出しですが、今回はここで終了です。