首页
论坛
课程
招聘
[原创]在调试器下观察Linux内核的数据包发送过程
2021-7-21 15:45 1247

[原创]在调试器下观察Linux内核的数据包发送过程

2021-7-21 15:45
1247

本文通过使用GDK7调试套件在Linux内核下观察数据包发送过程

1. TCP/IP参考模型与OSI参考模型

        TCP/IP参考模型与OSI参考模型的关系,如图1.1所示。 

图1.1 TCP/IP参考模型与OSI参考模型的关系

2. 观察信息发送的过程

2.1 应用程序层

        断下函数sock_sendmsg,并查看栈回溯,如图2.1所示。

图2.1 查看栈回溯

有信息发送时的栈回溯                       无信息发送时的栈回溯

lk!sock_sendmsg                                lk!sock_sendmsg

lk!sock_write_iter+0x8c                      lk!__sys_sendto+0x13f

lk!do_iter_readv_writev+0x14c          lk!__x64_sys_sendto+0x28

lk!do_iter_write+0x86                         lk!do_syscall_64+0x5a

lk!vfs_writev+0x98                             lk!entry_SYSCALL_64+0x7c

//通过观察图2.1中的栈回溯,我们可看到应用程序层内调用的函数信息及过程。

//在应用程序层内,进程通过使用套接字,将信息写入套接字内。

        使用套接字发送信息时,信息会到达系统调用的函数sock_sendmsg,与此同时也会检查用户缓冲区是否可读,如果可读,则通过套接字描述符(由发出调用的进程提供)获取结构体sock;然后它会根据所传输的信息创建消息头,并创建1个套接字控制信息(含有进程的UID、PID及GID)。

        函数sock_sendmsg被调用时,它遍历sendmsg函数,通过结构体proto ops中的sendmsg字段查询协议类型,并调用对应协议类型的sendmsg函数。

//TCP套接字=>调用tcp _sendmsg函数;UDP套接字=>调用udp_ sendmsg函数。

2.2 套接字层

       断下函数inet_sendmsg,查看栈回溯,如图2.2所示。

图2.2 查看栈回溯

//通过观察图2.2中的栈回溯,我们可看到套接字层内调用的函数信息及过程,显然函数inet_sendmsg被函数sock_sendmsg所调用。

       函数inet_sendmsg的代码信息,如图2.3所示。


图2.3 函数inet_sendmsg的代码信息

        套接字层内先是提取sock中的信息并检查信息的有效性,然后根据连接类型的不同(面向连接及无连接)去选择合适的协议(显然识别协议的类型也是套接字层的工作);接下来套接字层为各种套接字创建线程,方便进程调用。

2.3 传输层

       断下函数tcp_sendmsg。查看栈回溯,如图2.4所示。 

图2.4 查看栈回溯

//通过观察图2.5中的栈回溯,我们可看到传输层内调用的函数信息及过程,显然函数tcp_sendmsg被函数inet_sendmsg所调用。

       函数tcp_sendmsg会等待连接完成建立并为连接设置最大段的大小(不建立连接无法继续传输信息),最大段的大小如图2.5所示;建立连接之后会通过I/O结构发送信息,这也代表着用户数据会被传输到套接字的缓冲区中;TCP确认连接是否在规定时间内建立连接的代码信息,如图2.6所示。

图2.5 最大段的大小

图2.6 确认是否在超时前建立连接

//如果套接字的缓冲区被用户数据填满,那么函数tcp_sendmsg将会调用函数skb_copy_to _page函数将溢出的用户数据移动到内核空间中。

2.4 网络层

       断下函数ip_finish_output2,查看栈回溯,如图2.7所示。

图2.7 查看栈回溯

//通过观察图2.8中的栈回溯,我们可看到网络层内调用的函数信息及过程。

//函数ip_finish_output2根据目的IP到路由表里面找到下一跳的地址。

       网络层负责数据包的查找工作,并维护数据包的存货时间;函数ip_finish_outputskb进行分片判断,若需要分片则分片后输出,若不需要分片则直接输出;在分片工作完成以后,调用函数ip_rout_output_flow寻找路由并检查流结构是否为非零。

//函数ip_rout_output_flow利用流结构对数据包进行路由(快速检索路由关键在于搜索路由缓存信息,);流结构内存储流信息。

//先通过函数ip_rout_output_flow寻找路由,如果找不到则使用FIB寻找路由;如果在路由缓存/转发信息库中都找不到路由,则调用慢IP路由查找函数。        

//转发信息库:FIB(Forwarding Information Base)。

       数据包的路由由邻居缓存结构的输出结构完成,可分为3种情况:

//1.如果是要转发的数据包,那么邻居缓存结构的输出指针会指向函数ip_forword。

//2.如果数据包中存在未解析的路由,那么邻居缓存结构的输出指针会指向函数ip_output。

//3.如果存在被解析的路由,那么邻居缓存结构的输出指针会指向函数dev_queue_xmit。

2.5 网络接口层

        断下函数dev_queue_xmit,查看栈回溯,如图2.8所示。

图2.8 查看栈回溯

//从栈回溯中可以看到网络层在完成解析路由后会调用函数dev_queue_xmit,开始进入数据链路层工作范围,数据链路层主要负责将数据包传输给设备。

        函数dev_queue_xmit需要检查设备是否已经注册套接字的缓冲区及是否存在队列;在函数dev_queue_xmit拿到设备的队列锁之前本地下半部分将被禁用。

        通过调用I/O指令将数据包复制到硬件,然数据包将传输到物理媒体;在数据包传输完成后,设备将释放硬件中sk buff所占用的空间。

//传输失败,数据包会进入队列排队,等待重新传输。

参考文献

  1. Network_stack.pdf
  2. Lab9_modified.pdf
  3. https://blog.csdn.net/lee244868149/article/details/77653479?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control
  4. https://www.cnblogs.com/pipci/p/12909722.html

其他说明

若您有问题咨询及出现链接失效等其他情况请联系邮箱:birdring_001@outlook.com



第五届安全开发者峰会(SDC 2021)议题征集正式开启!

最后于 2021-7-21 15:50 被birdring编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回