linux网络协议栈分析——ioctl的调用流程

时间:2022-07-20 11:04:36

首先从系统调用开始,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是如何赋值的?再看一遍socket的创建过程:

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,
……
};

到此尚未结束,sock_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);
且看inetsw_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;
}