当前位置: 首页 > news >正文

Linux 网络

大家好,我是大聪明-PLUS

今天我将继续我们关于 Linux 内核的系列文章。

在本文中,我们将探讨服务器世界中网络的组织方式,以及它如何从使用传统的 Linux 内核网络堆栈发展到使用 OVS 的网络虚拟化,再到使用 NFV 和 SR-IOV 处理电信工作负载。

Linux 网络堆栈

在本文中,我们将探讨 Linux 内核中基本的 IPv4/TCP 流量流程,在深入探讨流量路径之前,我们应该先熟悉一些支持工具和概念:

环形缓冲区

当网络适配器启动并由内核加载其驱动程序模块时,驱动程序首先会在设备内存中分配 Rx(接收)和 Tx(发送)队列或缓冲区(称为环形缓冲区),这些缓冲区通常属于内核的 D​​MA 内存空间。您可以检查这些缓冲区的最大大小和已配置的大小:

$ ethtool -g INTERFACE_NAME 
Ring parameters for INTERFACE_NAME:
Pre-set maximums:
RX:             4096    <<<<<<<<<<<<<<<, Max size in bytes
RX Mini:        2048
RX Jumbo:       4096
TX:             4096    <<<<<<<<<<<<<<<, Max size in bytes
Current hardware settings:
RX:             1024    <<<<<<<<<<<<<<<, Configured size in bytes
RX Mini:        128
RX Jumbo:       512
TX:             512     <<<<<<<<<<<<<<<, Configured size in bytes

在早期的内核版本中,到达这些缓冲区的数据包会导致每个数据包都对 CPU 产生硬件中断,这非常具有侵入性,但幸运的是,引入了 NAPI 来帮助解决这个问题,您将在下面了解更多信息。

套接字缓冲区(sk_buff)

sk_buff(套接字缓冲区)  ,Linux 使用套接字缓冲区(sk_buff)与数据包/单元/段或任何其他接收的网络单元进行交互,并且每个 sk_buff 的数据和元数据(标头)是单独处理的,因此内核不需要在内存中移动数据包,套接字缓冲区充当存储桶,它存储段数据直到被处理,套接字缓冲区不会与数据包一起被销毁,它们会被释放并重新分配到新的数据包中,并且当它们用完时,CPU 会创建新的 sk_buff(新存储桶)来处理额外的流量。


注意:“sk_buff”和“skb”可以互换使用,而您会发现 skb 在组成 sk_buff 的内核代码中被广泛使用(如下图所示):

  • 数据包缓冲区(sk_buff 数据)  :实际数据包和数据在内核空间内存(DMA 内存空间 - DMA 概念将在下面讨论)中存储的位置,使用 sk_buff 结构指定,分配的 SKB 的大小等于 TCP MSS+Headroom,以允许 MSS 根据连接和用户修改而变化。

  • sk_buff 结构(套接字缓冲区元数据)  :有关存储在数据包缓冲区(数据)中的数据包的元数据,其中包括指针和值,它看起来像这样:请记住,这只是 sk_buff 结构的一部分

  1. 接口(input_dev)  :指数据包到达的接口的名称。

  2. 协议 :IPv4、IPv6等。

  3. Head  :指向 sk_buff 开始处的指针,它实际上以空白处开始,为附加标头(例如实例的 VLAN 标签)提供空间。

  4. 数据 :数据指针并不指向数据的开头,而是在堆栈函数中动态使用来获取和推送报头,因此例如在内核中当您说“推送以太网报头”时,实际发生的只是数据指针被移动到 IP 报头的开头,因此没有报头物理地放置在内存中。

  5. Tail  :表示数据部分的结束和 sk_buffer 空部分的开始。同样,由于 sk_buffer 的大小与配置的 MTU 相对应,因此此空部分用于不同大小的数据包。

  6. End  :指向内存中 sk_buff 的末尾。

  7. MAC、IP 和 TCP 标头指针  始终存储在 sk_buff 元数据中,允许直接调用它们,而无需执行 sk_buff 获取和发送步骤。

  8.  克隆 :您可以克隆 SKB 标头,但不能克隆数据。

注意:数据包不会在内核中复制;实际的数据包数据仍保留在数据包缓冲区中。每次克隆或复制时,数据包缓冲区保持不变;相反,会创建一个新的 sk_buff(又名 SKB),因此新的元数据指向现有的数据包缓冲区。即使数据包没有在内核中复制,它也会复制并到达应用程序,然后 SKB 会被释放。

sk_buff 的基本几何形状 

sk_buff— 是表示数据包的基本网络结构。

struct sk_buff本身是一个元数据结构,不包含任何数据包数据。所有数据都存储在链接的缓冲区中。

sk_buff.head指向主“头部”缓冲区。头部缓冲区分为两部分:

  • 包含标头(有时还包含有效负载)的数​​据缓冲区;这是 skb 的一部分,常规辅助程序(例如 skb_put()或 ) 可对其进行操作skb_pull()

  • 共享信息(struct skb_shared_info),其中包含指向格式为(页面、偏移量、长度)的只读数据的指针数组。

或者, skb_shared_info.frag_list它可以指向另一个 SKB。

基本图表可能如下所示:

                                ---------------| sk_buff       |---------------,---------------------------  + head/          ,-----------------  + data/          /      ,-----------  + tail
|          |      |            , + end
|          |      |           |
v          v      v           v-----------------------------------------------
| headroom | data |  tailroom | skb_shared_info |-----------------------------------------------+ [page frag]+ [page frag]+ [page frag]+ [page frag]       ---------+ frag_list    --> | sk_buff |---------

常见的 skb 和 skb 克隆 

sk_buff.users— 是一个简单的引用计数器,允许多个对象保持活动状态。带有的 skb 被称为共享 skb(请参阅 参考资料)。struct sk_buffsk_buff.users != 1skb_shared()

skb_clone()允许快速复制 skb。不会复制任何数据缓冲区,但调用者会收到一个新的元数据结构 ( )。&skb_shared_info.refcount 指定指向相同数据包的 skb 数量(即克隆)。struct sk_buff

没有标题的 dataref 和 skbs 

传输层会发送其存储的 skbs 有效载荷的克隆以供重传。为了允许协议栈的较低层添加自己的报头,我们将协议栈分 skb_shared_info.dataref成两半。低 16 位表示引用总数。高 16 位表示仅包含有效载荷的引用数量。 skb_header_cloned()检查 skb 是否被允许添加/写入报头。

skb 的创建者(例如 TCP)将其 skb 标记为 sk_buff.nohdr (via  __skb_header_release())。任何从标记的 skb 创建的克隆都将 sk_buff.hdr_len填充可用库存。如果存在单个克隆,它可以随意修改库存。传输层内的调用顺序如下:

<alloc skb>
skb_reserve()
__skb_header_release()
skb_clone()
// send the clone down the stack

这不是一个通用的设计,并且依赖于正确的传输层操作。实际上,通常只有一个仅用于负载的 skb。不可能存在多个具有不同 hdr_len 长度的仅用于负载的 skb。仅用于负载的 skb 不应离开其所有者。

校验和信息 

卸载堆栈和网络驱动程序之间的校验和的接口如下所示......

IP校验和相关函数 

驱动程序会在设备函数中通告校验和卸载功能。从堆栈的角度来看,这些功能是由驱动程序提供的。驱动程序通常只会通告那些可以传递给设备的函数。

与校验和相关的设备函数 

NETIF_F_HW_CSUM

驱动程序(或其设备)能够为任意协议或协议层组合计算单个 IP 校验和(二进制补码)。该校验和在 CHECKSUM_PARTIAL 接口数据包中计算并设置(见下文)。

NETIF_F_IP_CSUM

驱动程序(设备)只能验证 IPv4 上简单 TCP 或 UDP 数据包的校验和。这些数据包是 IPv4|TCP 或 IPv4|UDP 格式的未封装数据包,其中 IPv4 报头中的协议字段为 TCP 或 UDP。IPv4 报头可能包含 IP 参数。如果设备的函数中也设置了 NETIF_F_HW_CSUM,则无法设置此函数。此函数已弃用(见下文)。

NETIF_F_IPV6_CSUM

驱动程序(设备)只能验证 IPv6 上简单 TCP 或 UDP 数据包的校验和。这些数据包是 IPv6|TCP 或 IPv6|UDP 格式的未封装数据包,其中 IPv6 报头中的下一个报头字段为 TCP 或 UDP。此函数不支持 IPv6 扩展报头。如果设备的函数中也设置了 NETIF_F_HW_CSUM,则无法设置此函数。此函数已弃用(见下文)。

NETIF_F_RXCSUM

驱动程序(设备)卸载接收校验和。此标志仅用于禁用设备的 RX 校验和功能。无论 NETIF_F_RXCSUM 是否设置,协议栈都会接受设备接收到的数据包中的接收校验和。

设备收到的数据包的校验和 

校验和验证指示设置为 sk_buff.ip_summed。可能的值:

  • CHECKSUM_NONE

    设备无法对此数据包进行校验和,例如由于功能不足。该数据包包含完整的(但未经验证的)校验和,但在 skb->csum 中却没有。因此,在这种情况下,skb->csum 未定义。

  • CHECKSUM_UNNECESSARY

    您正在处理的硬件没有计算完整的校验和(如  [  [ CHECKSUM_COMPLETE... CHECKSUM_UNNECESSARYsk_buff.csum

    CHECKSUM_UNNECESSARY适用于以下协议:

    • TCP:IPv6 和 IPv4。

    • UDP:IPv4 和 IPv6。设备可以对 IPv4 或 IPv6 的 UDP 校验和为零的情况应用 CHECKSUM_UNNECESSARY,在这种情况下,网络堆栈可以执行额外的验证。

    • GRE:仅当校验和存在于标头中时。

    • SCTP:表示对SCTP头中的CRC进行验证。

    • FCOE:表示对FC帧中的CRC进行校验。

    sk_buff.csum_level指定数据包中找到的连续校验和的数量,减去已验证为 的一个校验和 CHECKSUM_UNNECESSARY。例如,如果设备接收到 IPv6->UDP->GRE->IPv4->TCP 数据包,并且该设备可以验证 UDP(可能为零)、GRE(已设置校验和标志)和 TCP 的校验和, sk_buff.csum_level则将设置 2。如果设备只能验证 UDP 校验和而无法验证 GRE 校验和(无论是因为它不支持 GRE 校验和还是因为 GRE 校验和不正确),则 skb->csum_level 将设置为零(在这种情况下,TCP 校验和将被忽略)。

  • CHECKSUM_COMPLETE

    这是最常用的方法。设备提供整个数据包的校验和,包括可见 netif_rx()和已填充的 sk_buff.csum部分。这意味着硬件无需分析 L3/L4 报头即可实现此操作。

    笔记:

    • 即使设备仅支持某些协议,但能够生成 skb->csum,它也必须使用 CHECKSUM_COMPLETE,而不是 CHECKSUM_UNNECESSARY。

    • CHECKSUM_COMPLETE 不适用于 SCTP 和 FCoE 协议。

  • CHECKSUM_PARTIAL

    校验和已配置,用于上传到设备,如 CHECKSUM_PARTIAL 的输出描述中所述。这种情况可能发生在直接从另一个 Linux 操作系统(例如同一主机上的虚拟化 Linux 内核)接收的数据包中,也可以在 GRO 的输入路径或远程校验和上传中设置。为了验证校验和,在 skb->csum_start + skb->csum_offset 中指定的校验和以及数据包中任何先前的校验和均视为已验证。校验和上传之后的数据包中的任何校验和均视为未验证。

NGSO 传输过程中的校验和 

协议栈请求卸载 sk_buff.ip_summed数据包的校验和。值:

  • CHECKSUM_PARTIAL

    驱动程序必须计算 hard_start_xmit() 所见数据包的校验 sk_buff.csum_start和,直到结束,并在 offset  sk_buff.csum_start+ 处写入/写入校验sk_buff.csum_offset和。驱动程序可以根据数据包长度和偏移量验证 csum_start 和 csum_offset 的值是否有效,但不应尝试验证校验和是否指向有效的传输层校验和——此验证由协议栈负责。请确保 csum_start 和 csum_offset 的设置正确。

    当协议栈请求卸载数据包的校验和时,驱动程序必须确保校验和设置正确。驱动程序可以将校验和计算卸载到设备上,或者调用 skb_checksum_help(如果设备不支持卸载特定校验和)。

    NETIF_F_IP_CSUM和 NETIF_F_IPV6_CSUM已弃用,取而代之的是 NETIF_F_HW_CSUM。应使用新设备 NETIF_F_HW_CSUM来指示校验和卸载功能。可以调用 skb_csum_hwoffload_help() CHECKSUM_PARTIAL来根据网络设备的校验和功能进行解析:如果数据包与它们不匹配,则调用 skb_checksum_help() 或 skb_crc32c_help()。

  • CHECKSUM_NONE

    skb校验和已经经过协议验证,或者不需要校验和。

  • CHECKSUM_UNNECESSARY

    这与 CHECKSUM_NONE 的含义相同,即在输出时卸载校验和。

  • CHECKSUM_COMPLETE

    输出校验和时不使用。如果驱动程序遇到在 skbuff 中设置了此值的数据包,则应按照此值已 CHECKSUM_NONE设置的方式处理该数据包。

无 IP CRC 卸载 

NETIF_F_SCTP_CRC

此函数指示设备能够卸载数据包中的 SCTP CRC。为了执行此卸载,协议栈分别设置 csum_start 和 csum_offset 的值,将 ip_summed 设置为 1  CHECKSUM_PARTIAL,并将 csum_not_inet 设置为 1,以向 skbuff 指示它们 CHECKSUM_PARTIAL引用的是 CRC32c。同时支持 IP 校验和卸载和 SCTP CRC32c 卸载的驱动程序应通过检查该值来检查为数据包配置了哪种卸载方式 sk_buff.csum_not_inet;skb_crc32c_csum_help() 旨在解析 CHECKSUM_PARTIALcsum_not_inet 设置为 1 的 skb。

NETIF_F_FCOE_CRC

此函数指示设备能够转储数据包中的 FCOE CRC。要执行此转储,协议栈将设置 ip_summed CHECKSUM_PARTIAL并相应地设置 csum_start 和 csum_offset。请注意,skbuff 并未指定哪个 CHECKSUM_PARTIAL转储指的是 FCOE 校验和,因此同时支持 IP 校验和和 FCOE CRC 转储的驱动程序必须验证为数据包配置了哪个转储,这可能需要检查数据包头。

使用 GSO 输出校验和 

对于 GSO 数据包(skb_is_gso() 为 true),gso_type 中的 SKB_GSO_* 标志隐含了校验和卸载。显然,如果 gso_type 等于 SKB_GSO_TCPV4或 SKB_GSO_TCPV6,则 TCP 校验和卸载隐含在 GSO 操作中。如果使用 GSO 卸载校验和,则 ip_summed 等于 CHECKSUM_PARTIAL,并且 csum_start 和 csum_offset 都指向最外层的已卸载校验和(使用 UDP 封装时,可能存在两个已卸载的校验和)。

内核中断(IRQ 或 SoftIRQ)

简而言之,中断用于停止处理器正在执行的任务,转而让中断程序处理任务。中断模型有很多种,每种模型又包含多种类型,但您可以看到,这些中断分为两类。

  • 上半部分中断(硬件中断)  :这种中断非常昂贵,因此中断处理程序在第一次使用后会屏蔽它们,然后网卡驱动程序开始使用 SoftIRQ(软件中断),它可以自行中断,您可以观察到这些中断:

$ cat /proc/interruptsCPU0       CPU10:        171          0   IO-APIC   2-edge      timer1:          0          9   IO-APIC   1-edge      i80428:          1          0   IO-APIC   8-edge      rtc09:          0         21   IO-APIC   9-fasteoi   acpi16:        494          0   IO-APIC  16-fasteoi   snd_hda_intel:card117:     372236       1575   IO-APIC  17-fasteoi   ehci_hcd:usb1, ehci_hcd:usb2, ehci_hcd:usb3, ath9k18:        268          0   IO-APIC  18-fasteoi   ohci_hcd:usb4, ohci_hcd:usb5, ohci_hcd:usb619:      49197       4164   IO-APIC  19-fasteoi   ahci[0000:00:11.0]25:          5     100550  PCI-MSI-0000:00:01.0   0-edge      radeon28:          0         28  PCI-MSI-0000:00:01.1   0-edge      snd_hda_intel:card0
NMI:          0          0   Non-maskable interrupts
LOC:    1852383    1696841   Local timer interrupts
SPU:          0          0   Spurious interrupts
PMI:          0          0   Performance monitoring interrupts
IWI:          1          0   IRQ work interrupts
RTR:          0          0   APIC ICR read retries
RES:     351662     366757   Rescheduling interrupts
CAL:     689212     789962   Function call interrupts
TLB:      19390      18975   TLB shootdowns
TRM:          0          0   Thermal event interrupts
THR:          0          0   Threshold APIC interrupts
DFR:          0          0   Deferred Error APIC interrupts
MCE:          0          0   Machine check exceptions
MCP:         23         23   Machine check polls
ERR:          1
MIS:          0
PIN:          0          0   Posted-interrupt notification event
NPI:          0          0   Nested posted-interrupt event
PIW:          0          0   Posted-interrupt wakeup event

每个中断(硬件中断)都由一个向量标识  该向量是一个单字节标识符,范围为​​ 0–255,从 0 到 31 是所谓的异常中断(不可屏蔽),范围 32–47 是可屏蔽中断,从 48 到 255 保留用于软件中断(SoftIRQ)。

简而言之,您会在上述输出中遇到三种类型的硬件中断:MSI-X、MSI 和旧式 IRQ。MSI 代表消息信号中断,它取代了以前使用处理器插槽中单个物理引脚为每个设备处理中断的方法。

  • 下半部中断(软中断)  :软中断会为每个处理器启动一个队列,您可以在 ps 命令的输出中找到它们,格式为 [ksoftiqd/CPU_Number]。这些队列轮询设备驱动程序来处理流量,而不是设备硬件(网卡)。通过在处理器每次接收到流量时进行中断,您可以看到接收和发送队列:

$ ps aux | grep ksoftirqd
root        15  0.0  0.0      0     0 ?        S    18:53   0:00 [ksoftirqd/0]
root        23  0.0  0.0      0     0 ?        S    18:53   0:00 [ksoftirqd/1]
argentum  5376 33.3  0.1   6588  2176 pts/0    S+   20:58   0:00 grep --color=auto ksoftirqd$ watch -n1 grep RX /proc/softirqs
Every 1,0s: grep RX /proc/softirqs
NET_RX:        396        337          0          0$ watch -n1 grep TX /proc/softirqs
Every 1,0s: grep TX /proc/softirqs
NET_TX:          1          1          0          0
其他概念
  • DMA(直接内存访问)  :NIC 设备是 PCIe 设备。以前,写入内存需要中断 CPU。因此,CPU 会将数据包复制到寄存器,然后将其写入内存。DMA 允许 CPU 在不中断 CPU 的情况下访问 DMA 资源。例如,当网卡 (NIC) 需要将以太网段写入内存时,它会使用 DMA 直接写入内存,而不会浪费宝贵的 CPU 周期。这些来自 NIC 的 DMA 写入调用会被主板上的北桥重定向到 RAM 而不是 CPU。

  • 环形缓冲区 :网卡驱动程序和网卡共享 TX 和 RX 环形缓冲区,这些缓冲区主要由指向数据包缓冲区内存位置的指针组成。这些缓冲区不包含数据,仅仅是内存指针。

  • 上半部分和下半部分 :当网络适配器通过 DMA 将数据包传输到内存(DMA 是内核内存中网络适配器无需 CPU 即可访问的位置)时,会向网卡发送 SoftIRQ(软中断请求),通知 CPU 有新数据包到达并等待处理。上半部分指的是 CPU 首先执行的操作,因此 CPU 不会停止所有进程来处理该数据包(这可能会造成干扰),而是直接确认中断,并将下半部分(处理该数据包所需的其余操作)安排到稍后执行。

  • 上下文切换:进程在用户空间上下文和内核空间上下文之间移动的过程,会消耗 CPU 周期。

  • 系统调用 。简单来说,系统调用是用户在用户空间中用来向内核空间请求服务的。

  • NAPI - Received Traffic  (新 API - 需要硬件支持)是网络设备的设备驱动程序扩展,旨在减少接收数据包时的中断次数。当接收到大量数据包时,中断仍会与正常的中断过程协同工作。它还有助于调节流量。如果网络适配器接收到过多的流量,NAPI 会在网络适配器级别丢弃数据包,而无需内核警报或中断。NAPI 仅对 数据包接收事件有效 。

    • SoftIRQ  :Linux 内核中的“softIRQ”系统是内核用来处理设备驱动程序 IRQ 上下文之外的工作的系统。设备驱动程序 IRQ(中断)通常对 Linux 内核具有最高优先级,并且在其他类型的中断到达时会抢占它们。KsoftIRQ 是一个队列 ,在内核中很早就作为线程为每个处理器启动,用于处理 softIRQ 的排队。您可以使用以下命令查看这些队列的计数器$ cat /proc/softirqs

    • ISR(中断服务程序)  :内核中的一项功能,负责确定中断的性质以及需要采取什么操作,之后 CPU 恢复处理先前暂停的进程。

网络接口

网络接口是设备与网络之间的通信通道。网络接口可以通过网络接口卡 (  NIC  ) 物理实现,也可以更抽象地通过软件实现。您可以同时运行多个网络接口。您可以随时启用(激活)或禁用(停用)特定接口。该实用程序会显示当前活动网络接口的列表 ifconfig。需要网络配置文件来确保接口正常运行。

对于 基于Debian的系统 ,基本网络配置文件为 /etc/network/interfaces。对于 基于RedHat的系统, 路由和主机信息包含在 中 /etc/sysconfig/network。网络接口配置脚本 eth0位于 /etc/sysconfig/network-scripts/ifcfg-eth0。对于 基于SUSE的系统 ,路由和主机信息以及网络接口配置脚本包含在 /etc/sysconfig/network目录中。

TCP/IP协议栈

网络究竟 是什么? 网络 是指两台以上的计算机通过 线路 或通信信道(在更复杂的情况下,通过网络设备)连接,并根据特定规则交换信息。这些规则由 TCP/IP 协议栈规定。

传输控制协议/互联网协议(TCP/IP 协议栈) 简而言之,是一组在不同层次上交互的协议( 每个层次都与其相邻的层次交互,也就是说,它们是互连的,因此 称为“栈”,在我看来,这样更容易理解),它们控制着网络上的数据交换。每个 协议 都是一组控制数据交换的规则。简而言之,  TCP/IP 协议栈 就是 一组规则集。 

 这里可能会出现一个合理的问题:为什么要有多个协议?我们不能用一个协议来与所有东西通信吗?

关键在于每个协议都描述了严格定义的 规则 。此外,协议被划分为不同的功能级别,使得网络设备和软件能够更简单、更透明地运行,并执行其特定的任务集。 

开放系统互连

为了将这组协议划分为多个层,  OSI 模型 ( 开放系统互连基本参考模型,  1978 年)应运而生。OSI 模型 由七个不同的层组成。每层负责通信系统的特定部分,并且独立于相邻层——仅提供特定的服务。每层都根据一组称为协议的规则执行各自的任务。OSI 模型可以用下图来说明: 数据是如何传输的?

该图显示 网络交互有七层,分为 应用层、表示层、会话层、传输层、网络层、数据链路层和物理层。每一层都包含一组各自的协议。

OSI协议

TCP/IP 协议栈本身随着 OSI 模型的采用而发展,并未与其“交叉”,因此协议栈与 OSI 模型各层之间存在细微差异。通常,在 TCP/IP 协议栈 中,OSI 模型的最顶层三层(应用层、表示层和会话层)合并为一层,即 应用层。由于这样的协议栈不提供统一的数据传输协议,因此数据类型确定功能被委托给应用程序。

寻址

在基于 TCP/IP 协议栈构建的网络中,每个主机(连接到网络的计算机或设备)都会被分配 一个 IP 地址。IP 地址 是一个 32 位二进制数。IP 地址 (IPv4) 的一种便捷书写形式是四个十进制数字(从 0 到 255),中间用句点分隔,例如 192.168.0.1。通常,  IP 地址分为两部分: 网络(子网)地址 和 主机地址

IP地址

存在 网络 和 子网。我认为从这些词的含义可以清楚地看出,IP 地址被划分为网络,而网络又通过 子网掩码 划分为子网(更准确地说, 主机地址可以划分为子网)。最初,所有 IP 地址都被划分为特定的组(地址类别/网络)。此外,还有分类寻址,根据分类寻址,网络被划分为严格定义、相互隔离的网络:

因特网

很容易计算,IP地址空间包含128个网络,共计16,777,216个A类地址;16,384个网络,共计65,536个B类地址;2,097,152个网络,共计256个C类地址;此外还有268,435,456个多播地址和134,317,728个保留地址。随着互联网的发展,该系统逐渐失效,并被 CIDR (无类随机访问寻址)所取代,其中网络中的地址数量由子网掩码决定。

IP 地址也 分为“私有”和“公共”。以下地址范围保留用于私有网络(也称为局域网):

  • 10.0.0.0 — 10.255.255.255 (10.0.0.0/8 或 10/8),

  • 172.16.0.0 — 172.31.255.255  (172.16.0.0/12 或 172.16/12),

  • 192.168.0.0 — 192.168.255.255  (192.168.0.0/16 或 192.168/16)。

  • 127.0.0.0 - 127.255.255.255 为环回接口保留(不用于网络节点之间的通信),即所谓的 localhost

除了主机地址之外,TCP/IP 网络还具有端口的概念。端口是系统资源的数值特征。端口分配给在网络主机上运行的应用程序,用于与其他网络主机上运行的应用程序(包括同一主机上的其他应用程序)进行通信。从软件角度来看,端口是由服务控制的内存区域。

对于每个 TCP 和 UDP 协议,标准定义了在一台主机上同时分配最多 65,536 个唯一端口的能力,这些端口用 0 到 65,535 之间的数字标识。端口号与使用该端口的服务之间的对应关系可以在  http://www.iana.org/assignments/port-numbers 中找到。所有端口分为三组:

  • 0 到 1023,称为特权或保留(用于系统和一些流行程序)

  • 1024 - 49151 称为注册端口。

  • 49151 - 65535 称为动态端口。

IP 协议在协议层次结构中位于TCP 和 UDP 之下,负责在网络上传输和路由信息。为此,IP 将每个信息块(TCP 或 UDP 数据包)封装在另一个数据包(IP 数据包或 IP 数据报)中,其中包含有关源、目标和路由的标头。

打个比方,TCP/IP 网络就像一座城市。街道和小巷名称是网络和子网。楼号是主机地址。在建筑物内,办公室/公寓号是端口。更准确地说,端口是收件人(服务)等待信件的邮箱。因此, 办公室端口号 1、2 等通常分配给董事和高管,因为他们享有特权,而普通员工的办公室端口号则更高。在发送和传递信件时,信息被打包到信封(IP 数据包)中,信封中标明了发件人的地址(IP 和端口)以及收件人的地址(IP 和端口)。简单来说,流程如下……

需要注意的是,IP协议没有端口的概念;TCP和UDP负责解释端口,同样,TCP和UDP也不处理IP地址。

为了避免记住像 IP 地址这样难以理解的数字字符串,而是将机器名称指定为人类可读的名称,一种名为 DNS(域名服务)的服务应运而生。它负责将主机名转换为 IP 地址,并像一个巨大的分布式数据库一样运行。我肯定会在以后的文章中详细介绍这项服务,但现在,只需知道,要将名称正确地转换为地址, 必须在计算机上运行named 守护进程,或者必须将系统配置为使用提供商的 DNS 服务即可。

路由

网络路由

让我们看一个包含多个子网的基础架构示例。你可能会想,一台计算机是如何连接到另一台计算机的?它如何知道将数据包发送到哪里?

为了解决这个问题,网络通过网关 (路由器) 连接起来 。网关 是连接到两个或多个网络的主机,能够在它们之间传输信息,并将数据包转发到另一个网络。

为了确定 数据包的路由,IP 使用地址的网络部分(子网掩码)。为了确定路由,网络上的每台机器都有 一个路由表 ,其中存储了网络及其网关的列表。IP 会查看数据包中目标地址的网络部分,如果路由表中存在该网络的条目,则数据包会被发送到相应的网关。

在 Linux 中,内核将路由表存储在 /proc/net/route文件中。您可以 使用netstat-m命令 (r 代表路由表,n 表示不将 IP 地址解析为名称)或route查看当前路由表。netstat -rn  输出的第一列 (目标 )包含 目标网络(主机)地址。指定网络时,地址通常以零结尾。 第二列(网关) 是第一列中指定的主机/网络的网关地址。 第三列(Genmask) 是路由有效的子网掩码。Flags  提供有关目标地址的信息(U 表示路由为 Up,N 表示路由用于网络,H 表示路由用于主机,等等)。  MSS 列 显示一次可以发送的字节数,  Window 是在收到确认之前可以发送的帧数,  irtt 是路由使用情况统计信息,  Iface 表示用于路由的网络接口(eth0、eth1 等)

正如您在下面的示例中看到的,第一个条目(行)是为 128.17.75 网络指定的。此网络的所有数据包都将被发送到网关 128.17.75.20,这是主机本身的 IP 地址。第二个条目是 默认路由,它适用于发送到此路由表中未指定的网络的所有数据包。在这里,路由经过主机 papaya(IP 128.17.75.98),它可以被认为是通往外界的大门。必须在 128.17.75 网络中所有需要访问其他网络的计算机上指定此路由。第三个条目是为 环回接口创建的。如果机器需要使用 TCP/IP 协议连接到自身,则使用此地址。路由表中的最后一项是针对 IP 128.17.75.20 创建的,并指向 lo 接口,即当将一台机器连接到地址 128.17.75.20 上的自身时,所有数据包都将发送到接口 127.0.0.1。

如果主机 eggplant 想要向主机 zucchini发送一个数据包(数据包会分别指示发送方128.17.75.20和接收方128.17.75.37),IP协议会根据路由表确定两台主机属于同一网络,并将数据包直接发送到 zucchini 接收该数据包的网络。更详细地说……网卡会广播一个ARP请求“IP 128.17.75.37是谁?是128.17.75.20吗?”所有收到此消息的机器都会忽略它,而地址为128.17.75.37的主机会回应“是我,我的MAC地址是某某……”。然后,基于包含IP-MAC地址对应关系的 ARP表,建立连接并进行数据交换。“某某”,意味着此数据包将发送给所有主机。出现这种情况的原因是接收方的 MAC 地址是广播地址 (FF:FF:FF:FF:FF:FF)。网络上的所有主机都会收到这样的数据包。

主机eggplant的路由表示例 :

[root@eggplant ~]# netstat -rn
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
128.17.75.0      128.17.75.20   255.255.255.0   UN        1500 0          0 eth0
default          128.17.75.98   0.0.0.0         UGN       1500 0          0 eth0
127.0.0.1        127.0.0.1      255.0.0.0       UH        3584 0          0 lo
128.17.75.20     127.0.0.1      255.255.255.0   UH        3584 0          0 lo

假设主机 eggplant 想要向pear 等主机发送一个数据包 ,甚至更远。在这种情况下,数据包的目的地是 128.17.112.21。IP 会 尝试在路由表中查找网络 128.17.112 的路由,但该路由不在路由表中。因此,  IP 会选择默认路由,并以 papaya  (128.17.75.98) 作为网关。接收到数据包后,  papaya 会在其路由表中查找目标地址:

[root@papaya ~]# netstat -rn
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
128.17.75.0      128.17.75.98   255.255.255.0   UN        1500 0          0 eth0
128.17.112.0     128.17.112.3   255.255.255.0   UN        1500 0          0 eth1
default          128.17.112.40  0.0.0.0         UGN       1500 0          0 eth1
127.0.0.1        127.0.0.1      255.0.0.0       UH        3584 0          0 lo
128.17.75.98     127.0.0.1      255.255.255.0   UH        3584 0          0 lo
128.17.112.3     127.0.0.1      255.255.255.0   UH        3584 0          0 lo

示例显示 papaya 连接到两个网络:通过 eth0 设备连接到 128.17.75 ,通过eth1设备连接到 128.17.112  。 默认路由通过主机 pineapple,而该主机又是通往外部网络的网关。

因此,在接收到发往pear的数据包时 ,  papaya 路由器将看到目标地址属于 128.17.112 网络,并将根据路由表中的第二个条目对数据包进行路由。

因此,数据包从一个路由器传递到另一个路由器,直到到达目标地址。

值得注意的是,在这些例子中,路线

128.17.75.98     127.0.0.1      255.255.255.0   UH        3584 0          0 lo
128.17.112.3     127.0.0.1      255.255.255.0   UH        3584 0          0 lo

这些都不是标准。在现代 Linux 中你不会看到类似的东西。

OVS-openvswitch

Open vSwitch (OVS) 是一款多层软件交换机。它旨在通过软件可扩展性实现大规模网络自动化,同时保持对标准接口和管理协议的支持。Open vSwitch 非常适合用作虚拟机环境中的虚拟交换机。

网络运营

关于互联网网络的资料可能比 Linux 的资料更多。不过,我仍会尽力讲解基础知识,并随着课程的进展,逐步触及与每个主题相关的各种网络细节。需要注意的是,我讲解得过于简化,因为这不是一门网络课程。我只会讲解我认为目前必要的内容。

过去,人们使用各种各样难以预测的 hack 和 prop(tuntap、brctl、vconfig、ebtables 等等)将标记流量转发到虚拟机管理程序。这导致虚拟机管理程序的主机操作系统充斥着大量不必要的虚拟网络接口,使 ifconfig 输出变得混乱,并且通常让管理员感到沮丧,因为他们需要像自行车一样,用各种不同的部件来构建一个标准的网络设备(交换机)。事实上,除了 802.1q 支持之外,如今的交换机还需要具备许多其他功能。对与标准现代托管交换机功能紧密匹配的虚拟设备的需求,促成了 Open vSwitch(以下简称 OVS)项目的诞生。

Open vSwitch 是一款基于 Apache 2 开源许可证的多层软件交换机。我们的目标是实现一个产品质量级的交换平台,支持标准管理接口,并开放转发功能以供软件扩展和控制。

Open vSwitch 非常适合用作虚拟机环境中的虚拟交换机。除了提供标准的管理界面和虚拟网络级别的可视性外,它还支持跨多个物理服务器的分布式部署。Open vSwitch 支持多种基于 Linux 的虚拟化技术,包括 KVM 和 VirtualBox。

大部分代码都是用独立于平台的 C 语言编写的,可以轻松移植到其他环境。Open vSwitch 的当前版本支持以下功能:

  • 具有中继和接入端口的标准 802.1Q VLAN 模型

  • 上游交换机上使用或不使用 LACP 的 NIC 绑定

  • NetFlow、sFlow(R) 和镜像可提高可视性

  • QoS(服务质量)配置和策略实施

  • 隧道 Geneve、GRE、VXLAN、STT、ERSPAN、GTP-U、SRv6、Bareudp 和 LISP。

  • 管理 802.1ag 连接错误

  • OpenFlow 1.0 及众多扩展

  • 具有 C 和 Python 绑定的事务配置数据库。

  • 使用 Linux 内核模块实现高性能转发

Open vSwitch 也可以完全在用户空间运行,无需内核模块的帮助。这种用户空间实现应该比基于内核的交换机更容易移植。用户空间 OVS 可以访问 Linux 或 DPDK 设备。注意:带有用户空间数据通道和非 DPDK 设备的 Open vSwitch 仍处于实验阶段,性能会有所下降。

该计划的主要组成部分是:

  • ovs-vswitchd 是一个实现切换的守护进程,也是一个用于基于线程的切换的配套 Linux 内核模块。

  • ovsdb-server 是一个轻量级数据库服务器,ovs-vswitchd 通过查询来获取其配置。

  • ovs-dpctl 是用于配置交换机内核模块的工具。

  • 用于为 Red Hat Enterprise Linux 构建 RPM 和为 Ubuntu/Debian 构建 deb 包的脚本和规范。

  • ovs-vsctl 是一个用于查询和更新 ovs-vswitchd 配置的实用程序。

  • ovs-appctl 是一个向正在运行的 Open vSwitch 守护进程发送命令的实用程序。

Open vSwitch 还提供了一些工具:

  • ovs-ofctl,用于查询和管理 OpenFlow 交换机和控制器的实用程序。

  • ovs-pki 是一个用于创建和管理 OpenFlow 交换机公钥基础设施的实用程序。

  • ovs-testcontroller 是一个简单的 OpenFlow 控制器,可能对测试有用(但不适用于生产)。

  • tcpdump 的修复程序允许分析 OpenFlow 消息。

虚拟机管理程序需要能够在虚拟机和外部世界之间桥接流量。在基于 Linux 的虚拟机管理程序中,这意味着使用内置的 L2 交换机(Linux 桥),它快速可靠。因此,有理由问为什么使用 Open vSwitch。

答案是,Open vSwitch 专为多服务器虚拟化部署而设计,而之前的技术栈并不适合这种环境。这些环境通常具有高度动态的端点、对逻辑抽象的支持,以及(有时)集成或卸载专用交换硬件。

以下特性和设计特点有助于 Open vSwitch 满足上述要求。

与网络对象(例如虚拟机)相关的所有网络状态都应该易于识别,并可在不同的主机之间传输。这可能包括传统的“软状态”(例如,L2 学习表中的条目)、L3 转发状态、策略路由状态、ACL、QoS 策略、监控配置(例如,NetFlow、IPFIX、sFlow)等。

Open vSwitch 支持实例之间慢速(配置)和快速网络状态的配置和迁移。例如,如果虚拟机在终端主机之间迁移,不仅可以迁移相关配置(SPAN 规则、ACL、QoS),还可以迁移任何活动网络状态(例如,包括可能难以恢复的现有状态)。此外,Open vSwitch 状态由真实的数据模型典型化和支持,从而支持结构化自动化系统的开发。

响应网络动态

虚拟环境通常具有快速变化的特点。虚拟机来来去去,虚拟机随时间向前或向后移动,逻辑网络环境发生变化等等。

Open vSwitch 支持多种功能,使网络管理系统能够响应和适应环境变化。这包括简单的会计和可视化支持,例如 NetFlow、IPFIX 和 sFlow。然而,Open vSwitch 对网络状态数据库 (OVSDB) 的支持或许更为实用,该数据库支持远程触发器。这使得编排软件能够监控网络的各个方面,并在发生变化时做出响应。例如,目前该功能正被积极用于响应和跟踪虚拟机迁移。

Open vSwitch 还支持 OpenFlow 作为流量管理的远程访问导出方法。这具有多种应用,包括通过发现流量或链路状态检查(例如 LLDP、CDP、OSPF 等)进行 WAN 发现。

逻辑标签维护

分布式虚拟交换机(例如 VMware vDS 和 Cisco Nexus 1000V)通常通过在网络数据包中添加或管理标签来维护网络内的逻辑上下文。这可以用来唯一地标识虚拟机(以抵御硬件欺骗的方式),或存储仅在逻辑域内有意义的其他上下文。创建分布式虚拟交换机的一大挑战在于如何有效且正确地管理这些标签。

Open vSwitch 包含多种用于指定和维护标记规则的方法,每种方法均可由远程进程访问并进行编排。此外,在许多情况下,这些标记规则以优化的形式存储,因此无需与繁重的网络设备关联。例如,这允许配置、修改和迁移数千条标记或地址重新分配规则。

同样,Open vSwitch 支持 GRE 实现,能够同时处理数千个 GRE 隧道,并支持创建、配置和禁用隧道的远程配置。例如,这可以用于连接不同数据中心的私有虚拟机网络。

设备集成

Open vSwitch 转发路径(内核内数据路径)旨在将数据包处理卸载到硬件芯片组,无论这些芯片组是安装在传统的硬件交换机机箱中,还是安装在终端主机的网卡中。这使得 Open vSwitch 控制路径能够同时管理纯软件实现和硬件交换机。

目前,许多将 Open vSwitch 移植到硬件芯片组的尝试正在进行中。这些尝试包括一些商用硅芯片组(Broadcom 和 Marvell),以及一些特定于供应商的平台。文档的“移植”部分讨论了如何实现此类移植。

硬件集成的优势不仅仅在于虚拟化环境中的性能。如果物理交换机也提供 Open vSwitch 管理抽象,那么物理和虚拟化托管环境都可以使用相同的自动化网络管理引擎进行管理。

在许多方面,Open vSwitch 在设计空间中瞄准的目标与以前的虚拟机管理程序网络堆栈不同,它专注于大规模基于 Linux 的虚拟化环境中对自动和动态网络管理的需求。

Open vSwitch 的目标是尽可能精简其内部代码(这对于提高性能至关重要),并在适用的情况下重用现有子系统(例如,Open vSwitch 利用现有的 QoS 堆栈)。从 Linux 3.3 开始,Open vSwitch 已包含在内核中,并且大多数主流发行版都提供了用户空间实用程序包。

您需要了解的有关 VLAN 的信息

VLAN 是存在于 OSI 模型第 2 层的虚拟网络,由第 2 层交换机实现。简单来说,VLAN 是由交换机端口组成的组,划分为多​​个逻辑网段。每个网段都有自己的标记(PVID 标签)。每组 VLAN 端口都通过这些标签来识别其所属的特定组。

端口有两种类型:接入端口和中继端口。接入端口用于连接终端网络设备,而中继端口仅用于连接其他中继端口。当来自计算机的数据包进入接入端口时,它会被标记 VLAN ID (PVID),然后交换机会仅将此数据包转发到具有相同 VLAN ID 的端口或中继端口。当帧发送到终端节点时,标记会被删除,因此终端节点不知道它位于哪个 VLAN 中。

数据包到达Trunk端口后,会直接进行传输,不会去除Tag,因此,带有多个Tag(PVID)的数据包可以在Trunk端口内传输。

值得注意的是,可以配置接入端口,使其在输出时不清除标签。在这种情况下,最终客户端必须知道它连接到哪个 VLAN 才能接受数据包。并非所有网卡和/或操作系统都默认支持 VLAN。这通常取决于网卡驱动程序。

安装和入门

OpenVSwitch 软件包包含在标准的 Ubuntu/Debian 仓库中,因此安装非常简单。运行以下命令:

sudo apt install openvswitch-switch

ovs-vsctl 实用程序用于操作网络接口。让我们检查一下安装了哪个版本的 OpenVSwitch:

sudo ovs-vsctl -V

实用程序输出:

ovs-vsctl (Open vSwitch) 2.9.0
DB Schema 7.15.1
创建虚拟接口

要创建虚拟交换机,首先需要创建到真实网络设备的桥接。真实网络端口本质上是虚拟交换机端口之一。

让我们创建一座桥梁:

sudo ovs-vsctl add-br bridgeswitch

向网桥添加网络接口:

sudo ovs-vsctl add-port bridgeswitch eth0

现在您可以继续添加虚拟端口:

sudo ovs-vsctl add-port bridgeswitch test-interface  -- set interface test-interface type=internal

在上面的命令中,我们指定创建一个名为 test-interface 的虚拟接口,并将其绑定到 bridgeswitch 网桥。在双斜杠(表示换行符)之后,我们指定了接口类型。

可以创建无限数量的虚拟交换机(例如虚拟接口)。这些虚拟网络接口随后将连接到虚拟机。

就这样。现在,运行以下命令:

sudo ovs-vsctl show

您可以看到所有网桥和虚拟接口的列表:

ovs-vsctl show
8e5433c6-c82d-46b0-8378-543c40e4e9c0
Bridge bridgeswitch
Port test-interface
Interface test-interface
type: internal
Port "enp5s0"
Interface "eth0"
Port bridgeswitch
Interface bridgeswitch
type: internal
ovs_version: "2.9.0"

test-interface 虚拟接口可以像常规物理接口一样处理,这意味着它可以在本地计算机上配置。当您需要让服务器使用单个网卡作为中继在多个 VLAN 上运行时,这非常有用。或者,例如,当您需要为服务器分配多个 IP 地址时。为此,请创建多个虚拟接口并分别进行配置。但是,我不建议这样做。如果出现上述情况,最好使用 systemd-networkd 或 ip。

要删除端口,请使用以下命令:

sudo ovs-vsctl del-port bridgeswitch test-interface

要拆除桥梁:

sudo ovs-vsctl del-br bridgeswitch
添加 VLAN

使用 OpenVSwitch 的一大优势是它支持 VLAN。为此,您只需标记虚拟端口并将网卡配置为中继接口即可。

让我们创建一个新的虚拟交换机:

sudo ovs-vsctl add-br vlanswitch

给虚拟交换机添加真实网卡:

sudo ovs-vsctl add-port vlanswitch eth0

我们将交换机端口设为中继端口,并描述将通过交换机的标签:

sudo ovs-vsctl set port eth0 trunks=10,20,300,400,1000

添加虚拟网络接口并为其分配标签:

sudo ovs-vsctl add-port vlanswitch testvlan20 tag=20 -- set interface testvlan20 type=internal

现在您可以查看配置:

ovs-vsctl show
8e5433c6-c82d-46b0-8378-543c40e4e9c0
Bridge vlanswitch
Port vlanswitch
Interface vlanswitch
type: internal
Port "testvlan20"
tag: 20
Interface "testvlan20"
type: internal
Port "enp5s0"
trunks: [10, 20, 300, 400, 1000]
Interface "enp5s0"
ovs_version: "2.9.0"
在 OpenVSwitch 虚拟交换机上启用 Netflow

如果需要跟踪通过接口的网络流量,您可以使用单个命令强制 OpenVSwitch 将 Netflow 数据包发送到收集器地址:

ovs-vsctl -- set Bridge vlanswitch netflow=@nf /
--   --id=@nf   create   NetFlow   targets="192.168.1.1:5566" /
active-timeout=30

您只需指定虚拟交换机的名称、IP 地址和目标端口以及发送数据的时间段(以秒为单位)。

要更新或更改上一个命令中指定的参数,请运行:

ovs-vsctl set Netflow vlanswitch active_timeout=60

要停止向 Netflow 收集器发送数据,只需清除虚拟交换机的 Netflow 设置:

ovs-vsctl clear Bridge vlanswitch netflow

网络基础知识

如果您正在阅读本文,您可能已经了解网络是什么。值得注意的是,几乎所有公司都有一些内部服务,并为其组织了所谓的局域网 (LAN)。我们稍后再讨论这个问题。为了使运行不同操作系统和程序的不同计算机能够相互交互,存在一个名为 OSI 的通用网络模型,它定义了标准。该模型将交互划分为多个步骤,称为层,每层都有各自的规则。这些规则定义了交互的具体发生方式,并称为协议。您可能在某处见过一些协议的名称——IP、DNS、HTTP。尽管标准 OSI 模型假设有七层,但管理员通常使用第二、三和四层,并将第五、六和七层合并为一层,通常称为第七层。

第一层是物理层,涉及物理交互的标准和技术。技术种类繁多,包括以太网、Wi-Fi 和光纤。为了连接到网络,每台计算机都配有网络适配器——通常是以太网适配器,笔记本电脑则配有 Wi-Fi。

您在工作中很可能会接触到以太网。为了将一台计算机连接到另一台计算机,需要使用特定类型的线缆,目前最常见的是 Cat 5 线缆。它由八根传输信号的铜线组成。这些线缆按照特定标准绞合成对,这样的一对线缆被称为双绞线。您的工作可能需要将这些线缆剪至所需长度,插入连接器并进行压接。

Cat 5 最高支持每秒 1 千兆比特 (1 Gbps)。Gbps 代表千兆比特每秒。网络速度以比特为单位,要理解吞吐量(以字节为单位),只需除以 8,因为 1 字节等于 8 比特。因此,理想情况下,Cat 5 可以以每秒 1024/8(即 128 兆字节)的速度传输文件。然而,由于环境因素的影响,实际速度会略低一些。

第二层称为数据链路层,计算机可以相互区分。为此,每个网络适配器的每个端口都有一个特殊的 MAC 地址。它由 12 个字母和数字组成,中间用两个冒号分隔,例如:00:1B:44:11:3A:B7。它是唯一的,由适配器制造商在出厂时设置。由于 MAC 地址可以识别其他计算机,因此可以将多台计算机连接在一起。虽然您可以拥有多台计算机,但在每台计算机上设置数百个端口并在计算机之间连接数千条线缆是不现实的。

交换机,通常称为交换机,通常用于将多台计算机连接到单个网络。这些设备最简单的形式是拥有大量端口——从 4 个到 96 个,更不用说一些罕见的端口了。如果端口数量不足,或者设备位于不同的楼层,则可以将多台交换机连接在一起。交换机至少有一个 MAC 地址表——交换机会记住每个 MAC 地址分配给哪个端口。当一台计算机想要连接到另一台计算机时,它会访问特定的 MAC 地址;交换机会识别该 MAC 地址,并通过其端口连接两台设备。

但这种方法适用于计算机数量不多的情况——十几台、一百台,不会更多。计算机数量可能很多,有些计算机的 MAC 地址可能会改变,比如更换网络适配器后,而使用 MAC 地址通常很不方便。因此,MAC 地址仅用于局域网。例如,在家中(如果您有多台设备),或者在小型办公室、大型公司的部门等等。想象一下一个小城市。每栋建筑都是一台计算机。局域网就是连接这些建筑的街道。

OSI 模型的第三层是网络层。它引入了 IP 地址的概念。局域网中的每台计算机都有一个 IP 地址,就像城市中的每栋建筑都有自己的地址一样。IP 地址允许不同网络上的计算机进行通信。事实上,它们也用于局域网;计算机只需使用 IP 地址找到相应的 MAC 地址即可。IP 协议有不同的版本:IPv4 和 IPv6。其中更流行、更简单的是 IPv4,我们将在后面讨论它。

IP 地址由四个用句点分隔的数字组成。这些数字的范围是 0 到 255。因此,从技术上讲,IP 地址由四个字节组成,每个字节可以包含八位,每位可以包含两个值。也就是 2 的 8 次方。这大约有 40 亿个地址。但这还不够——一台计算机可以有多个地址,每个虚拟机可以有自己的地址、电话和其他设备——简而言之,有很多东西。但是,网络允许不同的计算机拥有相同的地址,尽管它们不在同一个局域网内。

假设大多数人家里的 IP 地址都相同,比如 192.168.0.100。以城市为例,不同的城市可以有相同的地址。这在一个城市可能不太好,但在不同的城市则完全没问题。

为了帮助计算机了解哪些地址位于其所在网络,哪些地址位于其他网络,因此存在子网掩码。它列在 IP 地址旁边。它也由四个用句点分隔的数字组成,每个数字的值介于 0 到 255 之间。例如,我们以最常见的掩码(很可能是您在家中使用的掩码)255.255.255.0 为例。最后一个数字 0 表示您的网络可以有 256 个值,即 256 个地址。但是,第一个和最后一个值实际上是保留的。在这种情况下,0 用作网络地址,即城市本身的地址。而 255 是广播地址——一种用于寻址网络上所有计算机的方式。假设您的计算机的 IP 地址为 192.168.0.100,掩码为 255.255.255.0,那么它的本地网络将由地址从 192.168.0.1 到 192.168.0.254 的计算机组成,网络地址为 192.168.0.0,广播地址为 192.168.0.255。顺便提一下,掩码通常写为 /24。这意味着前三个数字是 255,这是 8 位的最大值。8 + 8 + 8 = 24。今天我们不会进一步讨论子网这个话题,我们继续。

为什么是 192.168?这是一系列保留的 IP 网络,专门用于家庭和企业私人用途。当您拜访其他人或企业时,您会遇到类似的子网和 IP 地址。

假设一台计算机发现某个 IP 地址位于另一个网络上。它如何访问该 IP 地址呢?这是通过一种称为路由的机制(通常称为“路由”)实现的。路由器通常用于此目的,例如您的家用路由器。它与您位于同一网络上,但也可以访问另一个网络,从而将您网络上的计算机与另一个网络上的计算机连接起来。路由器通常会分配边界 IP 地址(1 或 254),以明确表明它是路由器。在计算机上配置 IP 地址时,您还可以指定网关,然后输入路由器的地址。如果您指定了网关,则计算机在需要连接到另一个网络时会联系路由器,然后路由器将与该网络通信。但是,路由器不会传输 MAC 地址,只会传输 IP 地址。

互联网,最简化的形式,就是路由器和交换机的集合。您家中的路由器连接到 ISP 的路由器,而 ISP 的路由器又连接到其他 ISP 的路由器。所有这些连接构成了互联网。请看下图:家用电脑可以拥有相同的地址。您无法从互联网访问您的家庭网络 192.168.0.0,因为它是私人保留的,任何人都可以设置它。但是,您的路由器也有一个外部地址,例如 5.5.5.5。当您访问互联网时,路由器会将您的外部地址替换为其自己的地址,以便您能够收到响应。这项技术称为 NAT——网络地址转换。例如,如果某个网站收到来自 192.168.0.100 的请求,它将不知道将响应发送到哪里。但是,当您发出请求时,您的路由器会伪造 IP 地址,网站会收到 5.5.5.5 并进行响应。然后路由器将响应转发给您,因为使用 NAT,它会记住您的请求并等待响应。

同时,互联网上的网站或任何其他计算机都无法直接访问您的计算机,同样是因为 192.168.0.0 无法从互联网访问。其他计算机可以访问您的外部地址 5.5.5.5,但路由器本身不会将请求转发到您的计算机,因为这太危险了,因为互联网上有很多恶意软件。NAT 还允许多台计算机从单个外部 IP 地址访问互联网。例如,您的手机、计算机、笔记本电脑和电视都从同一个地址访问互联网。公司会使用这种方法来管理数百或数千台计算机。安全性和 IP 地址保护。

一家公司可能有多个网络,例如,一个服务器网络和一个用户网络。在这种情况下,通常不使用 NAT,因为确保用户能够访问服务器,并且服务器能够连接到用户的计算机非常重要。因此,公司不会对公司内的所有网络使用相同的 IP 地址。

所以,所有这些都是OSI模型的第三层——第三层,或者简称为l3。前三层构成了一张连接图,就像城市里的道路一样。城市里有很多房子,每栋房子都有自己的地址,每栋房子都位于一个城市里,要从一个城市到另一个城市,你需要先到城市出口,再从那里到下一个城市,然后再到你想要的房子。计算机——路由器——另一个路由器——另一台计算机。但网络的意义不在于到达某个地方,而在于传递数据包,或者用计算机的术语来说,就是数据包。计算机一需要将数据包传递给计算机二。

现在我们来到了第四层——运输。包裹的形式多种多样,投递方式也各有不同。例如,在美国电影中,邮递员会在早上把报纸送到家门口。如果刮风了,狗叼走了,或者其他原因——没什么大不了的,它只是一份报纸而已。假设这就是网络数据包传递(NTP)的工作原理。您的计算机可以向服务器请求当前时间,服务器会将响应发送回您的计算机。这种情况大约每10分钟发生一次。但是,如果您的计算机没有收到响应(可能是网络故障),也没什么大不了的。无论如何,您可以在10分钟后再次请求。这样一来,数据包就被发送出去了,但发送方无需知道它是否已送达。这种传递协议称为UDP。

但数据包通常包含重要信息,丢失信息并非所愿。在这种情况下,我们会使用另一种协议——TCP。在 TCP 连接中,发送方必须确保数据包已到达,并等待对方的确认。如果在一定时间内没有收到对方的响应,发送方会重新发送数据包的副本。

在这个层面上,我们还将学习端口。不是物理端口,而是 TCP 或 UDP 端口。如果说计算机是一栋房子,那么端口就是房子里的一间公寓。所有房子都是多公寓楼。你不能只是把包裹带回家就行——快递员必须到门口取货,然后送到门口。每扇门后面都住着一个特定的人,执行特定的任务。假设你通过浏览器访问网站,你的计算机会使用特定的端口发送请求。浏览器知道网站由位于端口 80 的 Web 服务器提供服务。因此,快递员会前往具有特定 IP 地址的特定房屋,敲开 80 号门。他会等待一段时间。如果 Web 服务器确实在那里,它会响应快递员,将数据包交给他,然后快递员会将响应返回到发送请求的端口。如果80号门没人,那这位可怜的快递员就会空手而归,浏览器会报告无法连接到Web服务器。Web服务器可能有65,000个端口,但通常只有3-4个用于接收,而发送端口则是动态分配的,取决于连接数量。

让我们快速介绍一下防火墙的概念。您可能不希望除了您自己或某些人之外的任何人连接到您服务器上的某些端口。您还可以在家门口安排一名保安人员接待快递员——如果有人不遵守保安的规定,保安人员会直接拒绝他们进入。这些规则通常规定哪些地址可以访问哪些端口,哪些不能。这些规则被称为个人防火墙,内置于系统本身。网络防火墙通常安装在路由器上——它们在网络入口处进行检查,就像城市入口处的安全哨所一样。

TCP 和 UDP 端口是应用程序本身(通常是系统上运行的守护进程)所在的端口。它们位于 OSI 模型的第 5、6 和 7 层。一个端口对应一个程序,但一个程序可以有多个端口。假设一个 Web 服务器同时使用端口 80 和 443。哪些程序使用哪些端口是有标准的。这些端口可以更改,但您需要知道自己在做什么。例如,如果您更改了 Web 服务器的默认端口,浏览器将无法打开网站,除非您在浏览器中手动指定端口。但普通用户并不知道这一点,他们又如何知道您更改了哪个端口呢?

程序已经在此级别运行,包括 Web 服务器和浏览器。这两个程序都使用 HTTP 等协议进行通信。正如浏览器可能多种多样一样,充当 Web 服务器的服务器上的程序也可能多种多样。正是使用单一的 HTTP 协议,才使得每个人都可以相互通信,无论浏览器、Web 服务器或操作系统是什么。

至此,我们简要介绍了 OSI 模型。当一台计算机尝试向另一台计算机发送数据时,这个过程类似于打包并发送。例如,您想在互联网上打开一个页面。您的浏览器会将您的请求打包到一个包含 HTTP 信息的盒子中。然后,您的操作系统会将这个盒子贴上另一个标签,上面写着 TCP 信息:发送请求的源端口和目标端口。之后,它会再贴上一个标签,上面写着请求的 IP 地址和目标 IP 地址。之后,再贴上一个标签,上面写着请求的 MAC 地址和目标 MAC 地址。计算机将接收到的数据包转换为 0 和 1,并将其发送到交换机。整个过程称为封装。交换机会转换数据包的开头部分,并查找要发送到的 MAC 地址——路由器。它会在 MAC 地址表中找到路由器的 MAC 地址——端口 12。交换机会将数据包发送到路由器。路由器会展开数据包,并查找要发送到的地址。假设路由器不知道这个地址,它会撕下三层标签,贴上自己的标签,用自己的 IP 地址替换你的 IP 地址以进行 NAT,并将你的标签保存在自己的存档中。然后,它会将你的 IP 地址发送到它的网关——另一个路由器。另一个路由器也会展开它,查看,如果不知道,就会将其发送到它的网关——第三个路由器。在这中间,会经过更多的交换机和路由器,这种情况会重复几十次,直到它到达正确的服务器。最终,它到达了正确的路由器,路由器会在其网络上找到正确的服务器,用 MAC 地址替换标签,并将它发送到正确的服务器。服务器将 0 和 1 转换成数据,看到 MAC 地址,并明白这是它自己的。它开始撕下更多标签,发现请求来自它的 IP 地址。它撕下另一个标签,看到 TCP 端口 80。防火墙会监控最后两个步骤——是否可以从这个 IP 地址向端口 80 发送数据包?如果是,数据包最终会到达 Web 服务器。然后,Web 服务器会准备响应,将其分发到不同的数据包中,开始封装,并发送到路由器。整个过程只需一瞬间,对用户来说透明,跨越了陆地和海洋的广阔距离。

好了,我们已经了解了两台计算机是如何通信的。现在值得一提的是两个重要的协议,它们确保了易用性。没有它们也可以工作,但使用起来很困难。我们先从 DHCP 开始。该协议允许动态分配 IP 地址。与 MAC 地址不同,IP 地址并非出厂时分配的;而是在每台计算机上单独配置的。但是,手动为每个用户分配不同的地址并非可行。首先,这需要专业知识;其次,当您拥有大量计算机时,这非常不方便。因此,网络中通常会存在一个 DHCP 服务器——它可以是家用路由器、智能交换机,甚至是独立的 Linux 虚拟机。当计算机连接到网络时,它会向网络上的所有设备发送一个特殊请求,询问网络上是否有任何 DHCP 服务器。DHCP 服务器收到此请求并响应用户。DHCP 会从可用的 IP 地址中决定分配哪个,并将该地址以及子网掩码、网关地址和其他设置提供给用户。在服务器上,IP 地址通常是静态分配的,即手动永久分配,并且在用户的计算机上动态分配。这意味着这些地址可能会在下次启动时发生变化。

第二个协议 DNS 将 IP 地址转换为名称,再将名称转换为 IP 地址,等等。您不会在浏览器中输入 百度 的 IP 地址吧?百度 拥有许多服务器,您如何知道以及为什么需要记住它们的 IP 地址?在浏览器中,输入要连接的域名 baidu.com。然后,浏览器会向特殊服务器发送 DNS 请求。这些服务器也是在您设置网络时配置的,可以由 DHCP 服务器提供。因此,如果您在浏览器中输入 baidu.com,浏览器会向系统中注册的 DNS 服务器发送包含此名称的请求。DNS 服务器要么知道此地址,要么联系其他 DNS 服务器,这些服务器也可能会继续联系该服务器,直到找到该名称。最终,DNS 返回 IP 地址,然后您的浏览器会向此地址发送 HTTP 请求。

网络是一个庞大而复杂的机制,但对最终用户来说,它却简化到了如同电灯开关一样的程度。在中大型公司,网络的管理者往往由整个部门的网络管理员组成,这些管理员都花费了多年的时间研究网络。

简单介绍一下网络流

我们继续我们的文章。

图 B: 简单解释一下分配的内容以及数据包如何流经内核:
1) 在内核启动过程的早期,CPU 会分配数据包缓冲区(RX 和 TX 缓冲区)并构建文件描述符。

2) CPU 通知 NIC 新的描述符已经创建,NIC 可以开始使用了。

3) DMA(直接访问存储器)检索描述符。

4) 数据包到达 NIC。

5) DMA 将数据包写入 RX 环形缓冲区。

6) NIC 通知驱动程序,驱动程序通知 CPU 新流量已准备好使用 硬件中断(IRQ)进行处理 。

7) 在第一个硬件中断之后,中断处理程序会屏蔽它,驱动程序改用 软件中断(SoftIRQ)  ,这对 CPU 来说要便宜得多(硬件中断不能被中断,这对 CPU 来说非常昂贵)。

8) 软中断唤醒 NAPI 子系统,后者调用网卡驱动程序的轮询功能。

9) CPU 处理传入的数据包。
10) 当软中断预算耗尽后,NAPI 系统将重新进入睡眠状态。如果软中断预算耗尽,CPU 将转到下一个任务,同时 /proc/net/softnet_stats 文件中的time_squeezed 计数器 将加 1。

初始化网络适配器时,驱动程序执行以下操作:
1)在内存(DMA 空间)中分配 Rx 和 Tx 队列。

2)启用 NAPI(默认情况下禁用)。

3)注册中断处理程序。

4)启用硬件中断。

http://www.dtcms.com/a/389423.html

相关文章:

  • 【LVS入门宝典】探秘LVS透明性:客户端如何“看不见”后端服务器的魔法
  • 23届考研-C++面经(OD)
  • 运维安全06,服务安全
  • C++篇(9)list的模拟实现
  • Bugku-宽带信息泄露
  • LeetCode 845.数组中的最长山脉
  • 分布式存储与NFS:现代架构选型指南
  • SpringBoot三级缓存如何解决循环依赖的问题
  • 火山引擎 veCLI 发布,开启智能开发新模式
  • UE学习记录11----地形数据获取等高线
  • 【C++】STL--priority_queue(优先级队列)使用及其模拟实现、容器适配器和deque(双端队列)了解
  • 数学差能学人工智能吗?
  • Verilog语法学习EP10:串口接收模块
  • 使用obs同步录制窗口的高质量游戏模式视频
  • Qt语言家的简单使用记录
  • Taro + vue3项目,如何生成安卓 apk 安装包
  • Hive HQL命令
  • 智慧医疗新纪元:快瞳科技如何用OCR技术重塑医疗单据处理体验
  • 4.1软件工程管理-CMM2软件项目规划-思考题
  • 知识图谱对自然语言处理深层语义分析的影响与启示:2025年研究综述
  • 4.1软件工程管理-CMM2软件项目规划
  • 《手搓动态顺序表:从数组到自动扩容的华丽转身》
  • 【Day 60】Linux-LVS负载均衡
  • bash zsh sh与shell 有什么关系
  • AI时代格局重构:2025 GEO服务公司Top3
  • GEO 优化重构数字营销格局 孟庆涛技术创新引领行业突破
  • 思迈特 Agent BI 发布,重构数据应用新范式
  • 重构组态软件边界:紫金桥如何实现原生跨平台?
  • 工作中的好奇心:Perplexity CEO的AI时代洞察
  • JsonCpp:高效序列化与反序列化指南