linuxネットワークプロトコルスタック分析-ioctlの呼び出しプロセス


まずシステム呼び出しから、ioctlのシステム呼び出しはfs/ioctlである.c中:
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;
}