数据结构
模块加载
static int __init tun_init(void)
{
ret = rtnl_link_register(&tun_link_ops);
ret = misc_register(&tun_miscdev);
ret = register_netdevice_notifier(&tun_notifier_block);
}
模块初始化时,主要注册一些操作函数和接口。
1. tun_link_ops
static struct rtnl_link_ops tun_link_ops __read_mostly = {
.kind = DRV_NAME,
.priv_size = sizeof(struct tun_struct),
.setup = tun_setup,
.validate = tun_validate,
.get_size = tun_get_size,
.fill_info = tun_fill_info,
};
tun_setup(struct net_device *dev),该函数在创建tun设备时,用来初始化设备。
2. tun_miscdev
static struct miscdevice tun_miscdev = {
.minor = TUN_MINOR,
.name = "tun",
.nodename = "net/tun",
.fops = &tun_fops,
};
static const struct file_operations tun_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read_iter = tun_chr_read_iter,
.write_iter = tun_chr_write_iter,
.poll = tun_chr_poll,
.unlocked_ioctl = tun_chr_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = tun_chr_compat_ioctl,
#endif
.open = tun_chr_open,
.release = tun_chr_close,
.fasync = tun_chr_fasync,
#ifdef CONFIG_PROC_FS
.show_fdinfo = tun_chr_show_fdinfo,
#endif
};
3. tun_notifier_block
static struct notifier_block tun_notifier_block __read_mostly = {
.notifier_call = tun_device_event,
};
向netdev_chain通知链中注册该接口,订阅以下事件:
enum netdev_cmd {
NETDEV_UP = 1,
NETDEV_DOWN,
NETDEV_REBOOT,
NETDEV_CHANGE,
NETDEV_REGISTER,
NETDEV_UNREGISTER,
NETDEV_CHANGEMTU,
NETDEV_CHANGEADDR,
NETDEV_GOING_DOWN,
NETDEV_CHANGENAME,
…
}
网络子系统初始化
在网络模块初始化的时候,注册网络数据软中断的执行函数
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
这里的tx和rx是相对于内核协议栈来说的:
NET_TX_SOFTIRQ:发送数据到用户空间
NET_RX_SOFTIRQ:从用户空间接收数据
net_tx_action:
qdisc_run,循环发送报文
qdisc_restart,
dequeue_skb,从缓存区中得到待发送的skb
sch_direct_xmit,
xmit_one,
netdev_start_xmit
ops->ndo_start_xmit,tun_net_xmit将skb加入socket的sk_receive_queue中
static const struct net_device_ops tun_netdev_ops = {
.ndo_uninit = tun_net_uninit,
.ndo_open = tun_net_open,
.ndo_stop = tun_net_close,
.ndo_start_xmit = tun_net_xmit,
.ndo_fix_features = tun_net_fix_features,
.ndo_select_queue = tun_select_queue,
.ndo_set_rx_headroom = tun_set_headroom,
.ndo_get_stats64 = tun_net_get_stats64,
};
net_rx_action:
napi_poll调用napi_struct的poll函数获取数据,poll函数注册的是tun_napi_poll,该函数轮询从socket的sk_write_queue中取出skb,传入协议栈。
tun_napi_poll
tun_napi_receive
napi_gro_receive
napi_skb_finish
netif_receive_skb_internal,将skb数据包送进协议栈。
虚拟网卡创建
1. 打开字符设备/dev/net/tun,获得文件描述符;
open操作执行tun_fops的 open函数(tun_chr_open)。该函数在内核中创建一个tun协议的socket,将该socket与打开的文件描述符做关联。tun_socket_ops为该socket的操作函数集,包含发送和接收接口:
/* Ops structure to mimic raw sockets with tun */
static const struct proto_ops tun_socket_ops = {
.peek_len = tun_peek_len,
.sendmsg = tun_sendmsg,
.recvmsg = tun_recvmsg,
};
2. 执行ioctl,传入命令字TUNSETIFF,该命令字的处理函数是tun_set_iff:
(1) alloc_netdev_mqs,如果执行ioctl的时候传入了IFF_MULTI_QUEUE,则调用该函数创建网卡设备,分配net_device和其private data(tun_struct)的内存,调用tun_setup初始化tun_struct。
(2) tun_net_init,初始化net_device
(3) tun_attach,设备上报
如果执行ioctl时传入了IFF_NAPI参数,则TUN驱动将创建napi_struct结构体,将注册一个NAPI的poll处理函数tun_napi_poll,该poll函数用于轮询地从缓冲区获取数据传入内核协议栈,即从tun设备文件描述符的socket的sk_write_queue中取出skb,传入协议栈。
(4) register_netdevice,向内核注册新创建的net_device,广播NETDEV_REGISTER事件。
从用户空间获取数据包
tun_get_user,从用户空间缓冲区读取数据packet,封装skb
如果使能了napi,则将一定数量的skb放入sock的sk_write_queue队列中,执行napi_schedule,该函数会触发软中断NET_RX_SOFTIRQ,中断处理函数取出skb发到协议栈,参考【网络子系统初始化】部分。
发送数据包到用户空间
tun_put_user将一定数量的skb放到缓冲区,触发软中断NET_TX_SOFTIRQ,参考【网络子系统初始化】部分。