linuxネットワークプロトコルスタック分析-ioctlの呼び出しプロセス
まずシステム呼び出しから、ioctlのシステム呼び出しはfs/ioctlである.c中:
続行:
続行:
では、ネットワークファイルシステムのf_opはどのように割り当てられますか?ソケットの作成手順をもう一度見てください.
sock_に沿ってmap_fdは続行しsock_を呼び出したattach_fd、さらにinit_を呼び出したfile、渡されるパラメータは:socket_file_ops、このパラメータはfile構造に値を割り当てます:
また、sock_attach_fdはまた別の操作を行いました.
socket_file_opsの内容:
これで終わりじゃないioctlは目的の操作を完了していません.
ではopsはどこから得られたのでしょうか.socketからinet_を作成しています.create関数では、inetswチェーンテーブルを巡回してプロトコル構造を取得し、sock->ops=answer->opsに保存します.本格的なopsはプロトコル初期化inet_init関数呼び出しinet_register_protosw、グローバル配列inetsw_arrayがinetswチェーンテーブルに初期化された:fs_initcall(inet_init); 見てみろArrayグローバル配列:
これは具体的なプロトコルのioctlに着きました.
わあ、この関数こそ私たちが注目しているポイントです.
SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
{
……
error = do_vfs_ioctl(filp, fd, cmd, arg);
……
}
続行:
/*
* When you add any new common ioctls to the switches above and below
* please update compat_sys_ioctl() too.
*
* do_vfs_ioctl() is not for drivers and not intended to be EXPORT_SYMBOL()'d.
* It's just a simple helper for sys_ioctl and compat_sys_ioctl.
*/
int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
unsigned long arg)
{
……
default:
if (S_ISREG(filp->f_path.dentry->d_inode->i_mode))//
error = file_ioctl(filp, cmd, arg);
else
error = vfs_ioctl(filp, cmd, arg);//
break;
}
return error;
}
続行:
static long vfs_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
……
if (filp->f_op->unlocked_ioctl) {
error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
if (error == -ENOIOCTLCMD)
error = -EINVAL;
goto out;
} else if (filp->f_op->ioctl) {
lock_kernel();
error = filp->f_op->ioctl(filp->f_path.dentry->d_inode,
filp, cmd, arg);
unlock_kernel();
}
……
}
では、ネットワークファイルシステムのf_opはどのように割り当てられますか?ソケットの作成手順をもう一度見てください.
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{
……
retval = sock_create(family, type, protocol, &sock);
retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK));
……
}
sock_に沿ってmap_fdは続行しsock_を呼び出したattach_fd、さらにinit_を呼び出したfile、渡されるパラメータは:socket_file_ops、このパラメータはfile構造に値を割り当てます:
file->f_op = fop;
また、sock_attach_fdはまた別の操作を行いました.
file->private_data = sock;
socket_file_opsの内容:
static const struct file_operations socket_file_ops = {
……
.unlocked_ioctl = sock_ioctl,
……
};
これで終わりじゃないioctlは目的の操作を完了していません.
static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
{
struct socket *sock;
struct sock *sk;
void __user *argp = (void __user *)arg;
int pid, err;
struct net *net;
sock = file->private_data;
sk = sock->sk;
net = sock_net(sk);
……
default:
err = sock->ops->ioctl(sock, cmd, arg);
/*
* If this ioctl is unknown try to hand it down
* to the NIC driver.
*/
if (err == -ENOIOCTLCMD)
err = dev_ioctl(net, cmd, argp);
break;
……
}
ではopsはどこから得られたのでしょうか.socketからinet_を作成しています.create関数では、inetswチェーンテーブルを巡回してプロトコル構造を取得し、sock->ops=answer->opsに保存します.本格的なopsはプロトコル初期化inet_init関数呼び出しinet_register_protosw、グローバル配列inetsw_arrayがinetswチェーンテーブルに初期化された:fs_initcall(inet_init); 見てみろArrayグローバル配列:
static struct inet_protosw inetsw_array[] =
{
{
.type = SOCK_STREAM,
.protocol = IPPROTO_TCP,
.prot = &tcp_prot,
.ops = &inet_stream_ops,
.capability = -1,
.no_check = 0,
.flags = INET_PROTOSW_PERMANENT |
INET_PROTOSW_ICSK,
},
{
.type = SOCK_DGRAM,
.protocol = IPPROTO_UDP,
.prot = &udp_prot,
.ops = &inet_dgram_ops,
.capability = -1,
.no_check = UDP_CSUM_DEFAULT,
.flags = INET_PROTOSW_PERMANENT,
},
{
.type = SOCK_RAW,
.protocol = IPPROTO_IP, /* wild card */
.prot = &raw_prot,
.ops = &inet_sockraw_ops,
.capability = CAP_NET_RAW,
.no_check = UDP_CSUM_DEFAULT,
.flags = INET_PROTOSW_REUSE,
}
};
これは具体的なプロトコルのioctlに着きました.
const struct proto_ops inet_stream_ops = {
……
.ioctl = inet_ioctl,
……
};
わあ、この関数こそ私たちが注目しているポイントです.
int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
int err = 0;
struct net *net = sock_net(sk);
switch (cmd) {
case SIOCGSTAMP:
err = sock_get_timestamp(sk, (struct timeval __user *)arg);
break;
case SIOCGSTAMPNS:
err = sock_get_timestampns(sk, (struct timespec __user *)arg);
break;
case SIOCADDRT:
case SIOCDELRT:
case SIOCRTMSG:
err = ip_rt_ioctl(net, cmd, (void __user *)arg);
break;
case SIOCDARP:
case SIOCGARP:
case SIOCSARP:
err = arp_ioctl(net, cmd, (void __user *)arg);
break;
case SIOCGIFADDR:
case SIOCSIFADDR:
case SIOCGIFBRDADDR:
case SIOCSIFBRDADDR:
case SIOCGIFNETMASK:
case SIOCSIFNETMASK:
case SIOCGIFDSTADDR:
case SIOCSIFDSTADDR:
case SIOCSIFPFLAGS:
case SIOCGIFPFLAGS:
case SIOCSIFFLAGS:
err = devinet_ioctl(net, cmd, (void __user *)arg);
break;
default:
if (sk->sk_prot->ioctl)
err = sk->sk_prot->ioctl(sk, cmd, arg);
else
err = -ENOIOCTLCMD;
break;
}
return err;
}