首页 > 其他分享 >详解网络数据包发送的过程

详解网络数据包发送的过程

时间:2023-12-08 23:03:07浏览次数:37  
标签:调用 里面 ip 发送 详解 IP output 数据包

在 ip_queue_xmit 中,也即 IP 层的发送函数里面,有三部分逻辑。

第一部分,选取路由,也即我要发送这个包应该从哪个网卡出去。

这件事情主要由 ip_route_output_ports 函数完成。接下来的调用链为:ip_route_output_ports->ip_route_output_flow->__ip_route_output_key->ip_route_output_key_hash->ip_route_output_key_hash_rcu。

ip_route_output_key_hash_rcu 先会调用 fib_lookup。FIB 全称是 Forwarding Information Base,转发信息表。其实就是咱们常说的路由表。

路由表可以有多个,一般会有一个主表,RT_TABLE_MAIN。然后 fib_table_lookup 函数在这个表里面进行查找。

路由就是在 Linux 服务器上的路由表里面配置的一条一条规则。这些规则大概是这样的:想访问某个网段,从某个网卡出去,下一跳是某个 IP。

一个著名的实现,就是内核模块 ip_tables。在用户态,还有一个客户端程序 iptables,用命令行来干预内核的规则。

详解网络数据包发送的过程_数据包

iptables 有表和链的概念,最终要的是两个表。filter 表处理过滤功能,主要包含以下三个链。

  • INPUT 链:过滤所有目标地址是本机的数据包
  • FORWARD 链:过滤所有路过本机的数据包
  • OUTPUT 链:过滤所有由本机产生的数据包

nat 表主要处理网络地址转换,可以进行 SNAT(改变源地址)、DNAT(改变目标地址),包含以下三个链。

  • PREROUTING 链:可以在数据包到达时改变目标地址
  • OUTPUT 链:可以改变本地产生的数据包的目标地址
  • POSTROUTING 链:在数据包离开时改变数据包的源地址

详解网络数据包发送的过程_数据包_02

在这里,网络包马上就要发出去了,因而是 NF_INET_LOCAL_OUT,也即 ouput 链,如果用户曾经在 iptables 里面写过某些规则,就会在 nf_hook 这个函数里面起作用。

从 ip_finish_output 函数开始,发送网络包的逻辑由第三层到达第二层。ip_finish_output 最终调用 ip_finish_output2。

在 ip_finish_output2 中,先找到 struct rtable 路由表里面的下一跳,下一跳一定和本机在同一个局域网中,可以通过二层进行通信,因而通过 __ipv4_neigh_lookup_noref,查找如何通过二层访问下一跳。

在 __neigh_event_send 中,激活 ARP 分两种情况,第一种情况是马上激活,也即 immediate_probe。另一种情况是延迟激活则仅仅设置一个 timer。然后将 ARP 包放在 arp_queue 上。如果马上激活,就直接调用 neigh_probe;如果延迟激活,则定时器到了就会触发 neigh_timer_handler,在这里面还是会调用 neigh_probe。

qdisc 全称是 queueing discipline,中文叫排队规则。内核如果需要通过某个网络接口发送数据包,都需要按照为这个接口配置的 qdisc(排队规则)把数据包加入队列。

最简单的 qdisc 是 pfifo,它不对进入的数据包做任何的处理,数据包采用先入先出的方式通过队列。pfifo_fast 稍微复杂一些,它的队列包括三个波段(band)。在每个波段里面,使用先进先出规则。

三个波段的优先级也不相同。band 0 的优先级最高,band 2 的最低。如果 band 0 里面有数据包,系统就不会处理 band 1 里面的数据包,band 1 和 band 2 之间也是一样。

数据包是按照服务类型(Type of Service,TOS)被分配到三个波段里面的。TOS 是 IP 头里面的一个字段,代表了当前的包是高优先级的,还是低优先级的。

pfifo_fast 分为三个先入先出的队列,我们能称为三个 Band。根据网络包里面的 TOS,看这个包到底应该进入哪个队列。TOS 总共四位,每一位表示的意思不同,总共十六种类型。

详解网络数据包发送的过程_数据包_03

解析了发送一个网络包的过程。

详解网络数据包发送的过程_数据包_04

这个过程分成几个层次。

  • VFS 层:write 系统调用找到 struct file,根据里面的 file_operations 的定义,调用 sock_write_iter 函数。sock_write_iter 函数调用 sock_sendmsg 函数。
  • Socket 层:从 struct file 里面的 private_data 得到 struct socket,根据里面 ops 的定义,调用 inet_sendmsg 函数。
  • Sock 层:从 struct socket 里面的 sk 得到 struct sock,根据里面 sk_prot 的定义,调用 tcp_sendmsg 函数。
  • TCP 层:tcp_sendmsg 函数会调用 tcp_write_xmit 函数,tcp_write_xmit 函数会调用 tcp_transmit_skb,在这里实现了 TCP 层面向连接的逻辑。
  • IP 层:扩展 struct sock,得到 struct inet_connection_sock,根据里面 icsk_af_ops 的定义,调用 ip_queue_xmit 函数。
  • IP 层:ip_route_output_ports 函数里面会调用 fib_lookup 查找路由表。FIB 全称是 Forwarding Information Base,转发信息表,也就是路由表。
  • 在 IP 层里面要做的另一个事情是填写 IP 层的头。
  • 在 IP 层还要做的一件事情就是通过 iptables 规则。
  • MAC 层:IP 层调用 ip_finish_output 进行 MAC 层。
  • MAC 层需要 ARP 获得 MAC 地址,因而要调用 ___neigh_lookup_noref 查找属于同一个网段的邻居,他会调用 neigh_probe 发送 ARP。
  • 有了 MAC 地址,就可以调用 dev_queue_xmit 发送二层网络包了,它会调用 __dev_xmit_skb 会将请求放入队列。
  • 设备层:网络包的发送会触发一个软中断 NET_TX_SOFTIRQ 来处理队列中的数据。这个软中断的处理函数是 net_tx_action。
  • 在软中断处理函数中,会将网络包从队列上拿下来,调用网络设备的传输函数 ixgb_xmit_frame,将网络包发到设备的队列上去。



标签:调用,里面,ip,发送,详解,IP,output,数据包
From: https://blog.51cto.com/key3feng/8741914

相关文章

  • Python 输入输出与文件处理: io、pickle、json、csv、os.path 模块详解
    Python提供了强大的输入输出和文件处理工具,通过io、pickle和json等模块,开发者可以轻松处理文件、序列化和反序列化数据,并在不同格式之间进行转换。在本文中,我们将深入介绍这些模块的用法和实际示例。1.io模块:强大的输入输出工具io模块提供了对文件I/O进行灵活处理的能力......
  • Python 输入输出与文件处理: io、pickle、json、csv、os.path 模块详解
    Python提供了强大的输入输出和文件处理工具,通过io、pickle和json等模块,开发者可以轻松处理文件、序列化和反序列化数据,并在不同格式之间进行转换。在本文中,我们将深入介绍这些模块的用法和实际示例。1.io模块:强大的输入输出工具io模块提供了对文件I/O进行灵活处理的能力......
  • Thread常见方法:interrupt 方法详解
    打断sleep,wait,join的线程这几个方法都会让线程进入阻塞状态打断sleep的线程,会清空打断状态,以sleep为例privatestaticvoidtest1()throwsInterruptedException{Threadt1=newThread(()->{sleep(1);},"t1");t1.start();sleep(0.5);t1.interrupt();l......
  • python发送邮件
    使用SMTP模块发送邮件importsmtplibfromemail.mime.textimportMIMETextfromemail.headerimportHeadermsg_from='[email protected]'#发送方邮箱passwd='nztjmkbfie'#填入发送方邮箱的授权码(登录qq邮箱账号生成授权码)msg_to=['[email protected]']#收件人邮箱......
  • Java ClassLoader、ContextClassLoader与SPI实现详解
    (目录)JavaClassLoaderClassLoader做什么的?​ 众所周知,Java或者其他运行在JVM(java虚拟机)上面的程序都需要最终便以为字节码,然后被JVM加载运行,那么这个加载到虚拟机的过程就是classloader类加载器所干的事情.直白一点,就是通过一个类的全限定类名称来获取描述此类......
  • React diff 算法详解
    代码参照React16.13.1什么是Diff在render阶段的beginWork函数中,会将上次更新产生的Fiber节点与本次更新的JSX对象(对应ClassComponent的this.render方法返回值,或者FunctionComponent执行的返回值)进行比较。根据比较的结果生成workInProgressFiber,即本次更新的Fiber节......
  • Vue2 的 diff 算法详解
    所谓diff算法,就是通过比对新旧两个虚拟节点不一样的地方,针对那些不一样的地方进行新增或更新或删除操作。接下来详细介绍节点更新的过程。首先进行静态节点处理,判断新旧两个虚拟节点是否是静态节点,如果是,就不需要进行更新操作,可以直接跳过更新比对的过程。再更新处理新老节点......
  • Vue3 diff算法详解
    Diff更新算法由于目前Vue3对于性能的优化做了很多的处理,所以其在更新时并不会对所有的节点都进行diff更新。目前会进行diff更新的有以下两种情况:v-for容器节点自写的render()函数还有一种特殊情况会进行无diff的按序更新,这种更新是全替换模式,非常耗时:无key值的v-for语句,......
  • 秦疆的Java课程笔记:64 面向对象 构造器详解
    类中的构造器也称为构造方法,世在进行创建对象的时候必须要调用的。并且构造器有以下两个特点必须和类的名字相同必须没有返回类型,也不能写void构造器必须掌握!一个类即使什么也没写,也会存在一个方法//写一个空的Person类=========================publicclassPer......
  • C#连接RocketMQ发送消息
    publicstaticvoidCreateMessage(stringcontent,stringserverHost,stringstrTopic,stringstrkey,stringstrTag){try{//mq对象using(varmq=newProducer(){......