目录
1.再谈网络转发
2.路由
举个例子
3.分片和组装
IP 层
[Linux#67][IP] 报头详解 | 网络划分 | CIDR无类别 | DHCP动态分配 | NAT转发 | 路由器
1.再谈网络转发
我们在上一篇文章中知道了路由器的功能有:
- 转发
- DHCP | 组建局域网
- NAT
组建局域网功能表现:在路由器里配无线网络,设置网络名称+密码。
组件局域网只能使用内网IP。路由器分为家用路由器、企业级路由器。
- 一般家用路由器用的是192.168.* 开头的内网IP
- 企业级路由器用的10. *
- 172.16-172.31开头的内网IP
- 下面再看运营商对应的情况
每个家都有自己的路由器,家里局域网构建好了,使用的IP地址是以192.168开头。 - 家里在入网的时候都是要用到运营商的网络,所以家里的路由器是直接通过网线连接家里附近的运营商
- 所以你家里的路由器一定横跨了两个子网,一个是你自己家里的子网,一个是运营商路由器组建的子网。
所以不要认为你的数据从你家路由器出去了就直接到公网上了,可能是到了运营商的子网,然后继续向上转发。
- 你家里的路由器横跨了两个子网,一个是对内连接你自己家里的子网,一个是对外连接运营商路由器组建的子网
- 所以路由器一般会配有两个IP,一个是子网IP(LAN口IP)(对内)表示的是这个是路由器自己构建的子网并且属于这个子网内第一台主机。
- 并且这个路由器对外连接是运营商给它构建的子网,所以配置另一个WAN口IP(对外)。
运营商自己的路由器也配有LAN口IP(对内)配置的是内网IP,WAN口IP(对外)配置的是公网IP直接连接到公网上了。
- 换句话说,我们的数据包在从主机出来的时候是先给家用路由器然后再交给运营商路由器做运营商自己的内网转发,当转发到一定程度,再把数据包转到公网
- 再由公网IP转给对应的提供服务的企业。当回来的时候也是用的WAN口IP。
我们再看看数据包转发流程:
不同局域网私有IP是可以重复的,只要保证局部性的唯一性。这样就可以用重复的IP入网了,所以它可以用来解决了IP不足的问题。
- 但是现在就有问题了,虽然不同局域网但用的是同样的IP,假设去访问抖音,首先判断这个请求不是属于同一个局域网的,然后就把请求给家用路由器等等,一路最终给了抖音,最后抖音怎么知道应该把服务给谁。是给那个局域网的192.168.1.201?回不来了?
- 所以就需要一种技术支持,经过路由器的时候要把当前请求的源IP替换自己路由器的WAN口IP。然后就知道把服务转到哪里了。
我们把经过路由器不断的在做源IP和WAN口IP替换的这种技术叫做NAT。
那现在怎么回来呢?这个问题,我们后面说。
我们现在可以知道的就是:
- 一个路由器可以配置两个IP地址, 一个是WAN口IP, 一个是LAN口IP(子网IP).
- 路由器LAN口连接的主机, 都从属于当前这个路由器的子网中.
- 不同的路由器, 子网IP其实都是一样的(通常都是192.168.1.1). 子网内的主机IP地址不能重复. 但是子网之间的IP地址就可以重复了.
- 每一个家用路由器, 其实又作为运营商路由器的子网中的一个节点. 这样的运营商路由器可能会有很多级, 最外层的运营商路由器, WAN口IP就是一个公网IP了.
- 子网内的主机需要和外网进行通信时, 路由器将IP首部中的IP地址进行替换(替换成WAN口IP), 这样逐级替换, 最终数据包中的IP地址成为一个公网IP. 这种技术称为NAT(Network Address Translation,网络地址转换).
- 如果希望我们自己实现的服务器程序, 能够在公网上被访问到, 就需要把程序部署在一台具有外网IP的服务器上. 这样的服务器可以在阿里云/腾讯云上进行购买.
现在我们有公网/内网(子网/局域网),子网划分的概念,我们画图建立一个宏观的网络拓扑结构。
- 简单点就以比特位划分网络,比如说从美国发一个请求目的ip是湖北武汉。
- 一看这个目的IP结合子网掩码一查发现不是自己国家的网络就发送到国际之间的路由器,一查发现是中国的,就转发给中国,然后我国国际路由器在根据结合自己的子网掩码一看,发现是湖北的就转发给湖北。
- 再由湖北这里的服务器结合ip和路由器的子网掩码一查发现是武汉的,就转给武汉了。
这是我们构建起来的公网,可是你会发现一个问题,IP地址再分不够了。
没关系,到地方上不是有很多的本地运营商吗?此时在由本地运营商划分不同的子网。然后弄一个公网入口路由器。
我们就可以理解成整个网络拓扑结构就类似这种的。真实网络更复杂!
2.路由
我们并不谈路由表是怎么形成的,这涉及到路由器和路由器之间通信的问题我们不考虑。
我们只考虑两点
- 来了一个数据包到了中间节点路由器中它应该如何路由
- 第二一个路由器中的路由器的构成应该是什么样子的。
我们在路由的时候,一定要告诉要经历的路由器你要去哪里。
- 也就是说目的IP是什么,我们知道IP = 目标网络 + 目标主机 ,所以大部分路由都是拿着目的IP,然后先找这个目标主机所在的子网。
- 所以在查找的时候一定有一张对应的路由表来查,这个路由表能够从我的数据报文的目的IP配上对应的子网掩码进行提取目的网络。
- 在通过查找子网的过程经过路由之后找到目标主机所在的子网,然后再子网中进行内网转发把数据交给目标主机。
举个例子
下面一个简单例子理解一下这个找的过程。
- 假如我刚下火车想要去清华大学,不考虑其他方法,只能问人然后根据别人的指示进行下一步动作。
- 你出火车站碰到一个大爷,问大爷我去清华大学该怎么走。
日常你问别人路怎么走无非就得到下面几种结果
- 1. 我不知道,别问我。
- 2. 我自己不清楚,但是我知道有人知道,你看到那个扫地大妈没有她在这里生活20年了她肯定知道,你去问她把。
- 3. 大爷知道怎么走,你先这样走,在那样走,然后再问别人。
- 4. 大爷说这里就是你要去的地方。
在刚才的例子里,我就是一个数据包
- 我涵盖了一个目的地址(清华大学),大爷或者大妈以及你未来可能遇见要问路的人就是一个一个路由器
- 当你在问大爷的时候,他肯定要动脑子想,该怎么走或者其他的。
- 而大爷脑海里这种全局的或者局部的路径情况我们称为路由器中的路由表,当你问大爷而大爷在思考的过程就是在查路由表的过程,给你反馈怎么走就是查找得到的结果。
在回到上面故事,看大爷给我的反馈,你问大爷大爷说我不知道别问我,现实生活中确实可能遇到这种情况,但是现在是一个数据包千里迢迢送到一个路由器,路由器说不要问我,我不知道,我也不知道谁知道,这是不合理的!
- 这是查找逻辑出现了问题,一个路由器怎么能拒绝报文了,它应该全心全意帮报文找到目标网络
- 现实生活中存在的在网络中是不存在的,一般一个路由器会想办法竭尽全力帮助这个报文。
- 即便它不知道也要想办法仍给下一个路由器去。所以第一种情况不存在。
第二种情况,大爷说我不知道但是我知道那个扫地的大妈知道。
- 所以路由器自己不知道这个报文去哪里,但是它必须知道在自己不确定的路径下它自己要有一个默认路由,大妈就是那个默认路由(下一跳默认网关)。
第三种情况,大爷告诉你怎么走,然后到了地方你在去问别人。
- 这叫做大爷明确知道你要去哪里的,方向没有错,但是更多细节大爷就不知道了。路由器把报文转发到下一跳路由器。
一般在路上问人的时候无非就是这三种场景,而在网络中转发报文时常见的就是第二,第三种。
- 第四种场景是经过不断的查找最终你遇到了清华大学门口保安大爷,你问大爷清华大学怎么走,大爷说这就是清华大学。你很高兴。
- 但是你想想你真的要来清华大学吗,实际上你要去的是清华大学(目标网络) 19号宿舍楼(目标主机)。
- 于是你问大爷请问19号楼怎么走,所以门口保安大爷说你这样走那么走就到了,所以你很快就找到了19号宿舍楼。
- 至此这就是你找到了目标网络所在的目标主机。门口保安大爷叫做该子网入口的路由器。
所以当实际在进行路由转发的时候,永远要经历的是先在路上进行路由,然后到达目标子网之后,在经过目标子网的入口路由器进行内网转发将数据送达目标主机。
- 路上路由时我们只看目标网络,到达目标网络之后在结合入口路由器将数据发送目标主机。
- 路由的过程, 就是这样一跳一跳(Hop by Hop) “问路” 的过程.
所谓 “一跳” 就是数据链路层中的一个区间. 具体在以太网中指从源MAC地址到目的MAC地址之间的帧传输区间.
IP数据包的传输过程也和问路一样.
- 当IP数据包, 到达路由器时, 路由器会先查看目的IP;
- 路由器决定这个数据包是能直接发送给目标主机, 还是需要发送给下一个路由器;
- 依次反复, 一直到达目标IP地址;
那么如何判定当前这个数据包该发送到哪里呢? 这个就依靠每个节点内部维护一个路由表;
- 路由表可以使用route命令查看
- Destination: 目标网络(和我这个路由器直接相连的子网)
Gateway: 下一跳路由器
Genmask: 子网掩码
Use Iface: 发送接口
这里的default就是默认网关,确实这个目的IP不是我同一个网段,是哪里也不清楚,就把它扔到默认网关里。
假设某主机上的网络接口配置和路由表如下:
- 这台主机有两个网络接口,一个网络接口连到192.168.10.0/24网络,另一个网络接口连到192.168.56.0/24网络;
- 路由表的Destination是目的网络地址,Genmask是子网掩码,Gateway是下一跳地址,Iface是发送接口
Flags中的U标志表示此条目有效(可以禁用某些 条目)
- G标志表示此条目的下一跳地址是某个路由器的地址
- 没有G标志的条目表示目的网络地址是与本机接口直接相连的网络,不必经路由器转发
转发步骤
- 遍历路由器
- 目的IP & 路由表中配置的Getmask,确定该报文要去的网络
- 对比结果 和 Destination
- 通过Iface接口发出报文!
转发过程例1: 如果要发送的数据包的目的地址是192.168.56.3
- 跟第一行的子网掩码做与运算得 到192.168.56.0,与第一行的目的网络地址不符
- 再跟第二行的子网掩码做与运算得 到192.168.56.0,正是第二行的目的网络地址,因此从eth1接口发送出去;
- 由于192.168.56.0/24正 是与eth1 接口直接相连的网络,因此可以直接发到目的主机,不需要经路由器转发;
转发过程例2: 如果要发送的数据包的目的地址是202.10.1.2
- 依次和路由表前几项进行对比, 发现都不匹配;
- 按缺省路由条目, 从eth0接口发出去, 发往192.168.10.1路由器;
- 由192.168.10.1路由器根据它的路由表决定下一跳地址;
其实我们说的子网掩码是配置进路由器的,路由器一定要及连一个或多个子网,所以每个子网对应的网络号和它这子网所配套的子网掩码都是路由器的条目
所以报文到达时拿着目的IP与对应条目的子网掩码 查路由表发现直接连接的子网号,三位一体就可以将我们的报文进行转发。
问:当进行数据包转发时,我们要去的目标网络号会不会变化?
会的。最终的网络号是确定的!因为每个路由器配置的子网掩码都不一样,所有网络号也在变化。
- 但只要子网掩码越来越长,那就意味着我们要去的网络号越来越具体,意味着淘汰网络越来越多,主机号越来越短,距离目标主机距离越来越短。
- 只有最终到了入口路由器,已经没有子网了,直接就是目标主机所在的子网,接下来进行内网转发。
路由表可以由网络管理员手动维护(静态路由), 也可以通过一些算法自动生成(动态路由).
例如距离向量算法, LS算法, Dijkstra算法等.这里可以自己了解一下。
3.分片和组装
IP报头我们认识的差不多了,还是剩下三个字段。
- 这是要结合下一层数据链路层来学习,不过我们可以先看结论。
真正在路由器上转发的确实是IP报文 - 但真正的在网线上跑的是MAC帧!
数据链路层,有一个MAC帧协议,它规定:自己的有效载荷不能超过 1500 字节(MTU,是可以修改的),也就是说上层传递下来的单个IP报文(IP报头+IP报文的有效载荷)不能超过1500字节。
MAC说IP你不能给我每次报文超过1500字节。但是IP能决定单个报文大小吗?
- 不能,它只是负责路由转发。那谁能?
- 真正在网络中决定单个报文大小的是TCP
- (TCP是传输控制协议,它规定什么时候发,发多少。。。)
那TCP怎么控制呢?以前学TCP我们知道滑动窗口=min(拥塞窗口,对方接收能力),那为什么还要在滑动窗口里分一个一个报文呢?
- 为什么不直接把数据打包成一个直接发送出去呢?原因就在于数据链路层不允许发这么大。具体后面说。
- 那TCP就是给IP3000字节报文让它发,MAC说每次就是不能超过1500字节,那IP只能自己想办法了。IP想出了一个办法分片与组装。不过IP分片和组装不是主流情况!大部分TCP会考虑IP的感受。
所以IP就有上面三个字段来支持分片和组装。
IP 层
分片:在自己的IP层,组装:对端的IP层。
TCP和MAC并不关心IP进行分片。
如何分片?如何组装?
分片不能单独分片还要考虑对端组装的问题。灵魂五连问,我们要相信计算机设计中,有问题就有答案
- 你怎么知道一个报文被分片了?
- 同一个报文的分片怎么都能被识别出来?
- 哪一个是第一个,哪一个是最后一个,有没有收全或者丢失?
- 那个在前,那个在后,如何正确的进行组装?
- 怎么保证我合起来的报文是正确的
我们先认识这三个字段,然后再回到上面的问题。
16位标识(id):
- 唯一的标识主机发送的报文.
- 如果因为数据链路层规则而导致IP被分片了, 那么每一个分片里面的这个id都是相同的.
3位标志字段:
- 有三个比特位
- 第一位保留(保留的意思是现在不用, 但是还没想好说不定以后要用到).
- 第二位置为1表示禁止分片, 这时候如果报文长度超过MTU, IP模块就会丢弃报文.
- 第三位表示"更多分片"
- 如果分片了的话, 是最后一个分片该位置会被置为0, 类似于一个结束标记。
- 如果分片后面还有分片该位置被置为1,表示后面还有分片. (一个IP报文被分片后,每一分片报文也都必须要有IP报头,因为每一个分片本质也是一个独立的报文)
13位分片偏移(framegament offset):
- 是分片相对于原始IP报文开始处的偏移. 其实就是在表示当前分片在原报文中处在哪个位置. 实际偏移的字节数是这个值 * 8(==32/4) 得到的.
- 因此, 除了最后一个报文之外, 其他报文的长度必须是8的整数倍(否则报文就不连续了).
这三个字段就能支持分片和组装。
怎么知道一个报文被分片了?
- 1.如果更多分片是1,就证明该标识的报文分片了
2.如果更多分片是0 && 片偏移量>0,说明是分片了( 为最后一个分片~),否则不是!
同一个报文的分片怎么都能被识别出来?
- 16位标识!
哪一个是第一个,哪一个是最后一个,有没有收全或者丢失?
- 更多分片是1 && 片偏移是0 —> 第一个
更多分片是0 && 片偏移>0 —> 最后一个
有没有收全或者丢失保证不了,但没关系排序后,只要保证当前报文的起始位置+自身长度=下一个报文中填充的偏移量。这样即使出错了哪一个报文丢了也知道。
那个在前,那个在后,如何正确的进行组装?
- 只要按照片偏移量进行升序排序即可!
怎么保证我合起来的报文是正确的!
- TCP和IP有校验和
1.分片好吗?
不好!
2.对TCP和UDP包括IP本身有什么影响?
- 一个报文被拆成了多个,任意一个报文分片丢失,就会造成拼接组装失败,进而导致对端对整个报文进行重传。因为TCP并不知道IP进行分片了。
其次一个数据包不丢包概率99%,分成3片,99%*99%*99% < 99% ,所以分片会增加丢包概率。
接下来我们自己试着分一下
- 传输层给网络层3000字节的 TCP 报文,但是数据链路层要求每个报文最大不超过1500字节。
- 到了网络层加上IP报头,IP报文3020字节。现在怎么分片呢?
首先无脑拿前面1500字节,注意这1500字节是包含原始IP报文的报头的!
然后3020-1500还剩下1520字节,可是这1520字节可是纯数据
- 将来再分的分片也要添加报头,因此只能从1520字节拿1480个字节,然后在添加20字节的IP报头,组成1500字节IP报文。
- 现在1520-1480还剩下40字节,在添加20字节IP报头,组成60字节IP报文。
实际上发送的有效数据 1480、1480、40 组装完成后,正好是TCP报文长度。
现在这些过程应该越来越清晰了。
- 从应用层调用send/write把数据拷贝到tcp发送缓冲区
- 然后tcp以滑动窗口方式进行发送到IP层
- 加上IP报头,可能会分片。
下一篇文章,我们将开始学习 数据链路层~
子网掩码
子网掩码的存在就像「快递分拣」和「小区门禁」的结合,核心是为了解决三个问题:节省地址资源、提升寻址效率和实现灵活管理。举个生活中的例子解释:
1️⃣ 为什么不能只用IP?—— 快递分拣的视角
假设你有一个IP地址 192.168.1.100
,如果直接用它找目标主机,就像快递员要记住全国每个家庭的具体地址才能送快递,效率极低。而子网掩码(如 255.255.255.0
)的作用是:
- ✅ 划分区域层级:掩码把IP拆成「网络号+主机号」(比如
192.168.1.0
是小区,.100
是门牌号)。路由器只需先找到「小区」,再找「门牌号」,效率暴增。 - ❌ 没有掩码会怎样?如果全网只有主机号,路由表会像一本全国所有家庭的地址簿,大到无法存储和查询。
2️⃣ 子网掩码的灵活性—— 可变长的「小区划分」
传统的IP分类(A/B/C类)像固定大小的「小区」:
- C类小区(掩码
255.255.255.0
)只有254户(比如小公司),但若某公司有300台设备,C类不够用,B类又浪费6万多个地址。 - ✅ 子网掩码的魔法:通过调整掩码长度(如
255.255.254.0
),可以把一个B类大区切割成多个「可变大小的小区」,按需分配,避免浪费。
3️⃣ 安全与管理—— 小区里的「门禁系统」
子网掩码还像小区的围墙:
- ✅ 隔离广播域:同一个子网内的设备广播(比如ARP请求)不会干扰其他子网,减少网络拥堵。
- ✅ 独立策略:IT管理员可以针对不同子网设置防火墙规则(比如财务部子网禁止访问外网)。
🌰 总结比喻
- IP地址 = 国家+省份+城市+街道+门牌号(但长度固定不灵活)
- 子网掩码 = 一把可伸缩的尺子,自由决定哪部分算「城市」,哪部分算「街道」,让寻址既高效又省资源。