1、网络系统调用
1.1、socket()
1.1.1、sys_socket()
1.1.2、socket fs
1.1.3、sock_create()
1.1.3.1、net_families的注册
1.1.3.2、net_proto_family->create()(AF_UNIX域的实现)
1.1.3.3、net_proto_family->create()(AF_INET域的实现)
1.1.3.4、net_proto_family->create()(AF_NETLINK域的实现)
1.1.3.5、net_proto_family->create()(AF_PACKET域的实现)
1.1.4、sock_map_fd()
1.2、bind()
1.2.1、sys_ bind()
1.2.2、socket->ops->bind()(AF_UNIX域,SOCK_STREAM Type的实现)
1.2.3、socket->ops->bind()(AF_UNIX域,SOCK_DGAM Type的实现)
1.2.4、socket->ops->bind()(AF_INET域,SOCK_STREAM Type,default protocol的实现)
1.2.5、socket->ops->bind()(AF_INET域,SOCK_DGRAM Type,default protocol的实现)
1.2.6、socket->ops->bind()(AF_INET域,SOCK_RAW Type,default protocol的实现)
1.2.7、socket->ops->bind()(AF_NETLINK域的实现)
1.2.8、socket->ops->bind()(AF_PACKET域,SOCK_DGRAM/SOCK_RAW Type的实现)
1.2.9、socket->ops->bind()(AF_PACKET域,SOCK_PACKET Type的实现)
1.3、sendto()
1.3.1、sys_ sendto()
1.3.2、socket->ops->sendmsg()(AF_UNIX域,SOCK_STREAM Type的实现)
1.3.3、socket->ops-> sendmsg()(AF_UNIX域,SOCK_DGAM Type的实现)
1.3.4、socket->ops-> sendmsg()(AF_INET域,SOCK_STREAM Type,default protocol的实现)
1.3.4.1、sock->sk_prot-> sendmsg()(AF_INET域,SOCK_STREAM Type,default protocol的实现)
1.3.4.2、tcp_write_xmit()
1.3.4.3、tcp_transmit_skb()
1.3.4.4、icsk->icsk_af_ops->queue_xmit()
tcp sock的icsk->icsk_af_ops指针在proto->init函数被调用时赋值。
可以看到tcp链接的icsk->icsk_af_ops->queue_xmit函数为ip_queue_xmit。
关于ip层往下的路由查找部分,代码实在是有点复杂,真的是有点看不清楚了。这里就略过不再详细分析了。
路由查找的基本原理就是根据ip数据包的目的ip地址,在系统的路由表中找到对应的发送网口net_device。在找到对应网口以后,在查找arp表项,找到目的ip对应的mac地址,再给数据包加上以太网链路层包头:源/目的mac地址和包类型。如果arp表中没有找到表项,还需要发送arp请求报文,进行arp解析。
进过上述的一番查找以后,skb中的各层次包头已经加上,发送的网络接口也已经确认。最终的skb->dst->output(skb)函数会调用dev_queue_xmit()。
1.3.4.5、dev_queue_xmit()
1.3.4.6、net_device->qdisc
从dev_queue_xmit ()中我们可以看到,如果网口驱动有队列的话会先吧skb数据包存放到队列,再出队列发送。
队列就存放在net_device-> qdisc,队列的作用是驱动对包实现排序和排优先级的功能。我们具体看看网口的队列是怎么实现的。
在注册网口驱动register_netdevice()->dev_init_scheduler(),会给网口队列一个初始空值。
而在ifconfig eth0 up启动网卡时,会给队列重新赋值。ioctl()->sock_ioctl()->dev_ioctl()->SIOCSIFFLAGS-> dev_ifsioc()->dev_change_flags()->IFF_UP->dev_open()->dev_activate():
1.3.5、socket->ops->sendmsg()(AF_INET域,SOCK_DGRAM Type,default protocol的实现)
1.3.5.1、sock->sk_prot->sendmsg()(AF_INET域,SOCK_DGRAM Type,default protocol的实现)
1.3.5.2、udp_push_pending_frames()
往下的分析是网卡驱动的发送数据过程,请参考tcp发送中的dev_queue_xmit ()一节。
1.3.6、socket->ops->sendmsg()(AF_INET域,SOCK_RAW Type,default protocol的实现)
1.3.6.1、sock->sk_prot->sendmsg((AF_INET域,SOCK_RAW Type,default protocol的实现)
1.3.7、socket->ops->sendmsg()(AF_NETLINK域的实现)
1.3.7.1、netlink驱动(pid=0的内核netlink sock)
在注册netlink驱动时,会把驱动注册成一个内核pid=0的netlink sock。
用户态的netlink sokcet向内核netlink驱动收发数据,其实是两个netlink sock之间的数据通讯:一个是用户态的netlink sock它的pid是bind()函数指定的,一个是内核态通过netlink_kernel_create创建的pid=0的netlink sock。
1.3.7.2、netlink驱动的发送
用户态netlink sock需要发送数据给内核态netlink驱动,只需要将目的pid=0,即可通过sendmsg()函数发送数据到netlink驱动sock的接收函数。
1.3.7.3、netlink驱动的接收
netlink驱动在接受数据时,需要发送数据给用户态的netlink驱动。直接调用netlink_unicast发送给用户态sock,相当于netlink sock(pid=0,内核驱动sock)发送数据给netlink sock(pid=xxx,用户态sock)。
1.3.8、socket->ops->sendmsg()(AF_PACKET域,SOCK_DGRAM/SOCK_RAW Type的实现)
1.3.9、socket->ops->sendmsg()(AF_PACKET域,SOCK_PACKET Type的实现)
1.4、write()
1.4.1、sys_write()
1.4.2、file->f_op->aio_write()(sock文件的实现)
在创建socket时,sock_map_fd()函数会给sock的file->f_op赋值为socket_file_ops。
__sock_sendmsg()函数以后的分析,请参考sendto一节。
1.5、send ()
1.5.1、sys_send()
Sys_send实际上就是调用sys_sendto实现的,请参考sys_sendto函数的分析。
1.6、sendmsg()
1.6.1、sys_sendmsg()
Sock_sendmsg函数的分析,请参考sendto()一节。
1.7、recvfrom()
1.7.1、sys_ recvfrom()
1.7.2、socket->ops->recvmsg()(AF_UNIX域,SOCK_STREAM Type的实现)
1.7.2.1、接收数据来源
AF_UNIX域的socket是用作进程间通讯的,recv数据的来至于对端socket的发送。在AF_UNIX域socket的发送函数sendto实现里面可以看到,发送sock根据目的地址找到目的sock,再把skb直接放入接收sock的接收队列sock->sk_receive_queue。
1.7.3、socket->ops->recvmsg()(AF_UNIX域,SOCK_DGAM Type的实现)
1.7.3.1、接收数据来源
AF_UNIX域的socket是用作进程间通讯的,recv数据的来至于对端socket的发送。在AF_UNIX域socket的发送函数sendto实现里面可以看到,发送sock根据目的地址找到目的sock,再把skb直接放入接收sock的接收队列sock->sk_receive_queue。
1.7.4、socket->ops->recvmsg()(AF_INET域,SOCK_STREAM Type,default protocol的实现)
1.7.4.1、sock->sk_prot->recvmsg()(AF_INET域,SOCK_STREAM Type,default protocol的实现)
1.7.4.2、接收数据来源
AF_INET域的socket接收数据来源于网卡驱动的接收数据,网卡驱动通过netif_receive_skb()函数提交原始数据给系统以后,系统的网络处理会根据数据包的ip地址、协议类型、端口找到对应的接收socket,并剥去下层的包头将实际数据送给socket的接收队列。
1.7.5、socket->ops->recvmsg()(AF_INET域,SOCK_DGRAM Type,default protocol的实现)
1.7.5.1、sock->sk_prot->recvmsg()(AF_INET域,SOCK_DGRAM Type,default protocol的实现)
1.7.5.2、接收数据来源
AF_INET域的socket接收数据来源于网卡驱动的接收数据,网卡驱动通过netif_receive_skb()函数提交原始数据给系统以后,系统的网络处理会根据数据包的ip地址、协议类型、端口找到对应的接收socket,并剥去下层的包头将实际数据送给socket的接收队列。
Udp socket和tcp socket的区别是,udp不用组包一次只会传送一个数据包,而tcp会组包而且一次返回用户期望的数据长度。
1.7.6、socket->ops->recvmsg()(AF_INET域,SOCK_RAW Type,default protocol的实现)
1.7.6.1、sock->sk_prot- recvmsg((AF_INET域,SOCK_RAW Type,default protocol的实现)
1.7.6.2、接收数据来源
AF_INET域的socket接收数据来源于网卡驱动的接收数据,网卡驱动通过netif_receive_skb()函数提交原始数据给系统以后,系统的网络处理会根据数据包的ip地址、协议类型、端口找到对应的接收socket,并剥去下层的包头将实际数据送给socket的接收队列。
Raw socket的区别是,raw收到的是ip层之上的数据,所谓的tcp、udp的头需要自己解析和组装,也是无连接的类似于udp。
1.7.7、socket->ops->recvmsg()(AF_NETLINK域的实现)
1.7.7.1、接收数据来源
AF_NETLINK域的socket接收数据来源其他netlink socket的发送,netlink socket数据在发送时会根据目的地址中的pid,查找到对端的netlink socket,直接把发送数据挂接到对端socket的接收队列。
1.7.8、socket->ops->recvmsg()(AF_PACKET域,SOCK_DGRAM/SOCK_RAW Type的实现)
1.7.9、socket->ops->recvmsg()(AF_PACKET域,SOCK_PACKET Type的实现)
1.8、read()
1.8.1、sys_ read()
1.8.2、file->f_op->aio_read()(sock文件的实现)
在创建socket时,sock_map_fd()函数会给sock的file->f_op赋值为socket_file_ops。
最终和recvfrom函数一样调到了sock->ops->recvmsg函数,再往下具体的分析可以参考recvfrom函数。
1.9、recv()
1.9.1、sys_ recv()
1.10、recvmsg()
1.10.1、sys_ recvmsg()
最终和recvfrom函数一样调到了sock->ops->recvmsg函数,再往下具体的分析可以参考recvfrom函数。
1.11、ioctl()
ioctl关注的是对网口的操作。而getsockopt和setsockopt关注对某个协议域的各个层次参数的配置。
1.11.1、sys_ioctl()
1.11.2、file->f_op->unlocked_ioctl()(sock文件的实现)
1.11.3、socket->ops->ioctl()(AF_UNIX域的实现)
1.11.4、socket->ops->ioctl()(AF_INET域的实现)
1.11.4.1、sock->sk_prot->ioctl()(AF_INET域,SOCK_STREAM Type,default protocol的实现)
1.11.4.2、sock->sk_prot->ioctl()(AF_INET域,SOCK_DGRAM Type,default protocol的实现)
1.11.4.3、sock->sk_prot->ioctl((AF_INET域,SOCK_RAW Type,default protocol的实现)
1.11.5、socket->ops->ioctl()(AF_NETLINK域的实现)
1.11.6、dev_ioctl()
1.11.6.1、dev_ethtool()
1.11.6.2、dev_ifsioc()
1.11.6.3、ifconfig ethxxx up
“ifconfig ethxxx up”up端口命令的实现是这样的:
可以看到是调用“SIOSIFFLAGS”ioctl命令重新配置网口的flag,“SIOSIFFLAGS”命令的解析就再dev_ifsioc()函数中,我们看到是调用了dev_change_flags()。
1.11.6.4、ifconfig ethxxx down
“ifconfig ethxxx down”up端口命令的实现是这样的:
由up端口的实现分析可以知道,down端口调用过程是dev_ifsioc()->dev_change_flags()->dev_close()
1.11.7、阻塞/非阻塞模式设置
以recv数据包为例,说明阻塞和非阻塞的实现。
1.12、setsockopt()
ioctl关注的是对网口的操作。而getsockopt和setsockopt关注对某个协议域的各个层次参数的配置。
1.12.1、sys_setsockopt()
1.12.2、socket->ops->setsockopt()(AF_UNIX域的实现)
1.12.3、socket->ops->setsockopt()(AF_INET域的实现)
1.12.3.1、sock->sk_prot->setsockopt()(AF_INET域,SOCK_STREAM Type,default protocol的实现)
1.12.3.2、sock->sk_prot->setsockopt()(AF_INET域,SOCK_DGRAM Type,default protocol的实现)
1.12.3.3、sock->sk_prot->setsockopt((AF_INET域,SOCK_RAW Type,default protocol的实现)
1.12.3.4、ip_setsockopt((AF_INET域,IP层命令和用户自定义命令的实现)
1.12.4、socket->ops-> setsockopt()(AF_NETLINK域的实现)
1.12.5、nf_register_sockopt()
nf_register_sockopt函数用来注册某个域自定义的setopt和getopt的函数操作集。
1.12.6、nf_setsockopt()
nf_setsockopt()函数调用nf_register_sockopt注册的自定义的setopt函数操作集
1.12.7、nf_getsockopt()
1.13、getsockopt()
ioctl关注的是对网口的操作。而getsockopt和setsockopt关注对某个协议域的各个层次参数的配置。
1.13.1、sys_ getsockopt()
Getsockopt函数和setsockopt函数完全类似,这里就不再具体分析了。
2、网络驱动
2.1、驱动函数
2.1.1、网口结构体分配函数alloc_netdev()
Ldd3实例驱动注册的setup函数snull_init():
2.1.2、网口驱动注册函数register_netdev()
2.1.3、网口数据发送函数net_device->hard_start_xmit()
以ldd3的实例代码为例。
2.1.4、网口数据接收函数netif_rx ()(中断方式)
以ldd3的实例代码为例。
“NET_RX_SOFTIRQ”在初始化net_dev_init()中注册为net_rx_action。
继续看看中断模式dev->poll函数process_backlog()的实现。
netif_receive_skb()就会进一步根据数据包的内容(协议、地址、端口)对数据包进行处理,后面的处理比较复杂,就不再进一步分析。
2.1.5、网口数据接收函数net_device–>poll ()(NAPI轮询方式)
以ldd3的实例代码为例。NAPI方式在有数据包来到时,并不会像中断方式那样讲数据拷贝进skb再启动netif_rx,而是直接使用本身的网口设备调用netif_rx_schedule(),启动“NET_RX_SOFTIRQ”软中断。
NAPI轮询模式dev->poll函数调用的是网络驱动注册的net_device->poll函数。
2.1.6、网口启动函数net_device–>open()
以ldd3的实例代码为例。核心是调用netif_start_queue()函数。
2.1.7、网口停工函数net_device–>stop()
以ldd3的实例代码为例。核心是调用netif_stop_queue()函数。
2.1.8、网口ioctl函数net_device->do_ioctl()
2.1.9、网口ethtool_ops函数net_device->ethtool_ops()
3、网络工具程序
3.1、ifconfig
ifconfig工具由net-tools-1.60.tar.bz2软件包提供。Ifconfig命令是最常用的网络接口命令,可以查看端口的状态,配置端口ip、mac地址,up/down端口等等功能。下面看看ifconfig具体的代码实现是怎么样的。
3.1.1、查看端口状态
接下来我们分析ifconfig查看网口状态的具体代码实现
3.1.1.1、main()
3.1.1.2、if_print()
“/proc/net/dev”文件中包含的网口信息格式:
在“/sys/class/net”目录中也包含同样的网口信息:
3.1.2、配置端口IP
“ifconfig ethxxx 192.168.1.1 netmask 255.255.255.0”
3.1.3、up端口
“ifconfig ethxxx up”
3.1.4、down端口
“ifconfig ethxxx down”
3.2、ethtool
ethtool工具由ethtool_3.0.orig.tar.bz2软件包提供。Ethtool配置网口更底层的参数,例如phy的自协商、速度、pause支持等等。
配置命令是通过ioctl的“SIOCETHTOOL”配置下去,ifr->ifr_data再带下去具体的ethtool命令。Ethtool需要网口驱动net_device-> ethtool_ops函数的支持。
3.3、service network start/stop
网络相关的命令还有“service network start/stop/restart”启动/停止网络,service xxx启动的命令是对应“/etc/init.d/”路径下的xxx脚本,所以其实network就是“/etc/init.d/network”脚本,大家可以自己去看看其实现细节。
3.4、xinetd
xinetd是常见的网络服务后台监控进程,xinetd并不提供具体的服务,在检测到有对应服务接入时,再启动具体的网络服务程序。
这里就不具体解析了。