数据链路层 ——MAC
目录
MAC帧协议
mac地址
以太网帧格式
ARP协议
ARP报文格式编辑
RARP
其他的网络服务或者协议
DNS
ICMP协议
ping
traceroute
NAT技术
代理服务器
网络层负责规划转发路线,而链路层负责在网络节点之间的转发,也就是"一跳"的具体传输。
MAC帧协议
mac地址
首先,在一个局域网内的主机,能不能直接通信?当然能,在同一局域网的主机不需要经过路由器。
局域网通信其实就是将数据广播(严格来说不叫广播)到局域网,然后局域网内每一个主机的数据链路层都能收到这个数据帧,但是除了目标主机,其他的主机在识别到这个数据帧的目的mac地址不是自己之后,会将报文丢弃。 所以最终还是只有目的主机收到这个报文。所以,这个过程我们在逻辑上也认为是定向传输。
那么就意味着,在局域网中每一个主机都要有唯一的标识符。
我们在不考虑虚拟化的情况下,每一台物理的主机,都必须配有一张网卡才能接入网络,而每个网卡都有自己的序列号(sn号),称为该网卡的mac地址,我们也将其称为这个主机的 mac地址。每张网卡的mac地址在全球范围内都是唯一的,mac地址就是 48 位的一个整数。
那么这里又有一个问题了,我们只需要一个能够在局域网内进行唯一标识的标识符就可以了,而mac地址虽然也可以做到,但是它是全球唯一的,是否大材小用了? 从逻辑上来说确实大材小用了。
我们也说了,在一个局域网内,IP地址也是唯一的,为什么不直接使用IP地址来标识唯一性?
mac地址是硬件地址,无法更改。而IP地址则是在主机入网时,由所在局域网的路由器动态分配的,同时也可以由网络管理员手动更改分配。 由于他是动态分配的,那么我们无法为一个主机分配一个固定的唯一标识,而是每次都是在变化的。 当然还有一个更重要的原因就是,我们前面讲过的NAT技术,将私有IP不断替换,最终在出口路由器替换为公网IP发送到公网,其实我们可以理解为在该路由器下面的所有的主机都共用这一个公网IP,这时候,IP其实并不能很好的标识唯一性。 同时,IP和MAC地址是互相协作的,各自有各自的功能或者职责。 后面我们还会讲报文如何从入口路由器转发到我们的局域网内的主机,这时候也需要用到 MAC地址和IP这样的二元组来标识唯一性以及互作key值。
以太网帧格式
这里的源MAC,目的MAC,CRC校验和这三个字段我们很好理解,但是这个两字节的类型是什么呢?
2字节的帧协议类型字段,用来表明数据的类型,或者说有效载荷的类型,是IP报文,还是其他的类型的报文? 一般这个字段有三个取值, 0x0800,0x0806,0x8035 ,分别对应IP报文,ARP报文,RARP报文。
那么说到这里我们也很容易知道,他是负责分用的,来表明解包之后的有效载荷该交给上层的哪个协议。而网络层就是IP协议,表明收到的是一个IP报文,如果是ARP或者RARP 的话,则不是交给网络层,这两个协议也是在链路层,只不过在MAC协议之上。
而ARP报文和RARP报文,后续我们会知道,他们是用于获取MAC或者IP地址的,报文的格式以及长度是固定的,只有28字节。 而由于MAC帧协议要求有效载荷最少需要 46 字节,所以这两种协议的报文封装成MAC帧的时候,MAC协议会在他们的报文基础上,再加上 18 字节的填充,来凑够46字节。
至于为什么有最短数据长度的限制,这与以太网的CSMA/CD(载波监听多点接入/碰撞检测)机制有关,我们不做了解,只需要知道:64字节的MAC帧长度是为了确保在最大长度的电缆上,信号传播到最远端并返回的时间足够长,使得发送方能够在发送完数据之前接收到可能的碰撞信号,从而采取相应的措施(如重发或放弃发送),以保证数据的正确传输。 而64字节的最短帧长度,减去MAC帧协议的报头之后,还剩下46字节,所以我们的数据部分至少需要46字节。
以太网帧格式搞清楚了,我们再来复盘一下所谓的局域网通信,首先局域网内的所有主机直接或者间接级联,每一台主机要进行网络通信,就需要先入网,也就是需要接入局域网,而主机的MAC地址就作为他在局域网中的唯一标识。 那么某一台主机在进行局域网内的通信的时候,将上层交付给MAC协议的数据(假设是IP报文) 封装成MAC帧,MAC帧的报头中会携带发送者和接收者的MAC地址,以及帧类型 0800 ,还有帧校验和,然后通过网卡将数据发送到局域网中。
那么局域网中的每一台主机都会受到这个MAC帧,但是并不一定要把数据交给自己的上层。 每一台主机的链路层在收到一个MAC帧的时候,首先会进行解包,将MAC帧的报头提取出来,判断其中的目的MAC地址是不是自己的MAC地址,如果是,就将数据交给上层协议。 如果不是发给自己的,那么链路层就会直接将报文丢弃,而不需要交给上层处理。 那么与此同时,除了接收方的主机,局域网内其他主机的上层根本就不会知道在链路层曾经收到了一个不是发给自己的MAC帧,当然,本来就不应该知道。
结论就是: 局域网中所有的主机其实都能收到其他主机通过局域网发送的MAC帧,主机在自己的数据链路层通过对比MAC帧的目的MAC地址和自己的MAC地址是否相等,来决策是否要将数据交给上层协议处理。
既然局域网内所有主机都能收到MAC帧,我们能不能通过某些对网卡或者说MAC协议的设置,无需进行MAC地址的比对,直接将数据交给上层处理,也就是说,不管是不是发给自己的,都交给上层。
这就叫做网卡的混杂模式。
混杂模式的特点就是不放弃任何数据帧,直接向上交付。
这也是大部分的局域网抓包工具的原理,就是将网卡设置为混杂模式,然后不断向上交给对应的抓包工具。
同时,这也是为什么数据需要加密的原因,如果我们的重要数据也使用 http 协议进行明文传输,那么不说其他的,只要同一局域网内的主机,开启混杂模式,就能将你的报文拿到。 所以我们就需要是用 https 进行数据的加密。
那么也就意味着,局域网是被局域网内所有的主机所共享的,如果两个主机同时发送MAC帧,这时候就可能会出现 碰撞/干扰 的问题。 那么基于上面的问题,就意味着:
在局域网当中,任何时刻,只能有一个主机发消息。
但是这时候就有一个问题,我们作为普通的入网的主机,能控制自己的发送的行为,但是无法控制别人的发送行为,也就是说,就算我们千算万算,选择一个时间发送出去,但是也架不住局域网内的其他的主机同时发送数据帧,最终也还是会发生碰撞问题。
虽然不能禁止其他的主机发消息,但是我们能够检测是否发生了碰撞。 如果数据帧在局域网中发生了碰撞,那么会导致这个数据成为无效数据,怎么判断呢? 很简单,MAC帧协议的CRC校验,如果CRC校验失败或者说不通过,就说明数据帧发生错误了,也就是发生碰撞了。我们也说了,一个主机发出去的数据帧,局域网内的所有主机都能收到,那么就包括发送方自己,发送方在发送出去之后,可以在后续在接受自己发出去的这个MAC帧,这个MAC帧可能是绕着局域网一圈之后回到起点的,那么这时候他就可以通过判断这个MAC帧的完整性和正确性来判断是否发生碰撞。
但是检测发生了碰撞并不是解决问题的方法,而是发现问题,最终要解决问题还是要限制同一时刻不能有其他的主机发消息。
那么怎么保证在一个时刻只允许一个主机发送呢? 解决方案不同就有了不同的局域网的实现方案。
其中一种解决方案就是 令牌环。 类似于在我们的局域网中流转一部分令牌数据,只有持有令牌数据的主机才可以发消息, 同时,每一台主机发完消息之后需要把令牌通过局域网交给下一台主机。
还有一种方法就是分时使用,NTP/SNTP,简单理解就是局域网内所有主机统一一个时间段大小,将这个时间段划分为多份,每一份分配给一台主机,每个主机只能在分配给自己的时间内进行通信。这种策略需要局域网内所有主机有一个相同的时间标准,需要进行精确的时间同步。
还有就是基于碰撞域的以太网。
以太网其实就是在发生碰撞时,碰撞双方检测到碰撞之后,不立马进行重传,而是都进行一个随机时间的等待,等待之后再进行数据帧的重发。 同时,这两台主机在等待的时候,其他的主机就可以减少碰撞的概率。 当然即便进行等待之后,再次重传还是可能发生碰撞,但是再次发生碰撞的概率很低很低。 就算发生了,还可以再次重发。
所以,重传策略不只有TCP有,MAC协议也有重发策略。
网络资源就跟我们学习操作系统时所说的共享资源一样,局域网资源被局域网内所有主机共享,同一时刻只能有一台主机像局域网中发送消息。而我们所说的令牌环就类似于系统中的锁,只有拿到锁的主机才能使用共享资源。
现在最常用的局域网就是以太网,后续我们如果不特别指明,说的局域网都是指的以太网。
局域网能不能很大?
不能,因为局域网是一个碰撞域,如果局域网过大,主机过多的话,就会导致发生碰撞的概率增加。
那么如果没办法,一个局域网内就是有很多的主机呢?这时候怎么减少碰撞的概率呢?
那么我们就需要讲一种设备,交换机。
在大多数情况下,我们像局域网中发送一个消息,其实并不想让太多主机收到,最好就是只有目标主机收到,而当局域网很大的时候,如果我们发送一个消息,这个消息会在整个碰撞域内传输,就算我们的消息已经被目标主机接收了,也还是会做一些无用的传输。
交换机的作用我们可以理解为将局域网这个碰撞域又进行了划分,分割了碰撞域。
那么有什么好处呢?
首先,最直观的就是,如果我们的数据帧不需要跨交换机,那么交换机就不会对其转发。
比如主机A发送报文给主机D,那么这个报文就只会在碰撞域 1 中进行传输,在数据帧到达交换机之后,交换机检测到目的主机在碰撞域1中,那么就不会将其转发到碰撞域2。
于此同时,就允许碰撞域1内的通信和碰撞域2内的通信同时进行,提高了发送的效率。
如果主机A要发送报文给主机E,那么也很简单,跨交换机就行了。
还有一种情况就是可以拦截碰撞数据。
比如主机A发送给 主机F的报文,在碰撞域1中发生碰撞了,这时候碰撞之后的报文还是会继续在局域网内传输,但是遇到交换机的时候,检测到数据帧发生碰撞,不会对其进行转发。因为碰撞之后的报文已经是无效的数据了,没必要转发他浪费网络资源。
因为碰撞域的存在,一台主机发送数据的时候,发送数据量大更好,还是发送数据量小更好?
数据量小更好。 因为数据量越大,意味着在局域网中的传输的时间就越长,那么发生碰撞的概率就会变大,同时发生碰撞之后补发的成本也很大。 如果数据量小的话,在局域网中的传输时间就很短,发生碰撞的概率就越小,同时就算发生了碰撞,补发的代码也很小。
当然在另一方面,如果我们发送的数据的总量固定,那么一次性发送的数据量小的话,意味着发送次数要变多,当然这个影响比不上大量数据的重传。
由于一次发送的数据量不能太大,所以MAC帧协议规定了我们的有效载荷的长度不能超过 1500 字节,如果超过 1500 字节就需要在IP层进行分片与组装。 这个最大长度就算 MTU ,最大传输单元。
不同类型的网络的 MTU 可能不同。
那么就意味着,不仅是发送方主机的IP层要进行分片与组装,路上的路由器也可能需要进行分片与组装。
因为假设我们的报文(1500字节)从一个 MTU 为1500 的网络,经过路由器,转发到了一个 MTU 为 1000 的网络中,那么其实我们的报文是不能直接被转发的,而是需要在这个路由器进行分片。 路由器也是工作在网络层的,所以他也有分片的功能。
但是也有一种情况,因为我们转发的路径可能不止一条,如果我们的报文或者分片报文被设置了禁止分片的标记位,那么路由器也会根据这个信息,选择一条无需分片就能转发的路径。
但是如果只有一条路径,MTU比较小,同时报文被设置了不可分片标记位,那么路由器也没办法,只能将其交给这唯一的一条路径,然后该报文就会被链路层丢弃。
那么这里我们也能看出来,IP报头的禁止分片标记位也是影响路由选择的策略的因素之一。
MTU对于TCP和UDP的影响
对于UDP而言,UDP的报文的大小取决于用户层交给他的数据的大小,UDP的报头长度为8字节,IP报头标准长度为 20 字节 ,那么就意味着只要上层交给 UDP的数据超过 1472 字节,那么就需要IP曾对其进行分片。 而一旦进行分片了,任何一个分片丢失了,接收方的IP层都会将其丢弃,也就意味着丢包的概率增大了。 而由于UDP没有重传机制,所以我们也无法确定是否丢包。
所以再使用UDP协议的时候,为了降低丢包的概率,我们要尽可能的避免分片,这就需要用户层对发送的数据的大小进行控制,如果一次性发送的数据的大小超过了最大的限制,那么就需要用户层手动将报文拆成多个发出去,同时也需要接收方的应用层自己组装。
而如果是TCP协议的话, 由于TCP是传输控制协议,那么就意味着一次性发送多少数据并不由应用层控制,而是由TCP协议控制。 而一旦一次性交给IP层的数据太多,IP也需要进行分片,那么也就意味着丢包的概率增大了。
TCP虽然有重传机制,但是对于这种大数据的重传,成本还是很高的。同时TCP要保证可靠性,同时也要保证高效,丢包的概率增大了,也就间接导致传送效率低 。 所以TCP为了保证可靠性和高效性,也要对一次交给IP层的报文进行控制,尽量减少分片。
而TCP和IP的标准报头都是 20 字节,就意味着一次最多发送 MTU - 40 字节的数据。 我们把一次发送有效数据的最大大小称为 MSS(max segment size ,最大段尺寸)。
TCP在三次握手建立连接的时候,除了会交换双方的初始窗口大小之外,也会进行 MSS 的协商,最终双方通信时的MSS就是二者中较小的MSS值。 MSS 是在选项中携带的,因为在整个通信过程中,只需要交换一次MSS,后续都不再需要,所以没必要将其设为固定字段。
这就是为什么我们在讲TCP滑动窗口的时候,即使对方的接收能力很大,我们最终发送报文的时候也是一个一个1kb左右的报文发出去,就是因为MSS的影响。
ARP协议
在路由转发的过程中,MAC地址是不断在发生变化的,同时发送方发送数据的时候,也只需要知道对方的IP和端口号就行了,并不需要填入对方的MAC地址,MAC地址是在子网或者局域网的时候用的,最终数据帧被转发到了目标网络的路由器中。 但是有这样一种情况,就是路由器不知道目标主机的MAC地址。 这是有可能的,因为网络中十岁时可能会加入进来新主机的,尽管路由器会进行IP的分配,但是这也仅能代表路由器知道在他所管理的子网或者局域网内存在对于IP的主机,而不一定知道对方的MAC地址。
这时候就需要让路由器通过一定的方法,通过IP地址,获取对方的MAC地址。
因为只有知道了MAC地址,才能在链路层封装到目标MAC地址字段,然后通过局域网发给对方。
而ARP协议就是在一个局域网中,获取陌生主机的MAC地址协议。
ARP报文格式
硬件类型其实就是网络类型,绝大多数情况下都是以太网,这个字段的值就是1,而硬件地址长度就表示的是以太网的地址长度,其实就是 MAC地址的长度,那么他的值就是 6 。而协议类型则表示的是我们需要通过什么协议的地址获取硬件地址 ,绝大多数情况下已知的都是 IP 地址,所以协议类型就是IP协议,那么这个字段的值就是 0x8080 。而协议地址长度就是我们已知或者说依据的地址的长度,IP地址的长度就是4字节,所以这个字段一般填的是4。
前面的四个字段我们可以当成固定字段来理解。
而发送方的MAC地址和IP地址,这一点很好理解。 接收方IP地址也好理解,但是这里的接收方MAC地址怎么理解呢? 我们不正是因为要获取对方的MAC地址才使用ARP协议的吗?他这里又需要我们填写MAC地址,怎么办呢?
那么我们先暂停一下,剩下的两个字段我们一会再分析,我们先来认识影响ARP协议以及arp的过程。
首先,当我们只知道对方的IP,而不知道对方的MAC时,我们需要发送一个ARP请求报文,但是由于我们不知道对方主机到底是哪个,所以我们需要的是将这个报文在局域网内广播,让所有的主机都受到并且处理我们的arp请求,所有的主机都需要判断自己是不是我们要请求的主机。
而目标主机在收到这个请求报文之后,就需要进行应答,他需要封装一个ARP应答报文,由于在他收到的ARP请求中已经包含了发送方的IP和MAC地址,那么他进行应答的时候就不需要进行广播,只需要进行一对一的发送。
所以arp的两个阶段: 1、arp请求,局域网内广播
2、arp应答,目标主机一对一把应答发给请求方。
我们要搞清楚一个问题:
是谁需要目的主机的MAC地址?
如果发送方和接收方都在同一局域网内,且发送方知道接收方的IP,却不知道对方的MAC地址,那么他也需要使用ARP协议来获取对方的MAC地址。
如果发送方和接收方不在一个局域网,那么就是目的主机所在网络的路由器需要知道目的主机的MAC地址,也需要使用ARP协议。
我们把路由器也当作主机看,其实就是同一局域网内,主机之间的通信,只知道IP地址,但是不知道MAC地址的时候,需要使用ARP协议来获取对方的MAC地址。
但是这还不够准确,是那一层协议需要对方的MAC地址呢?
答案很明显,链路层。
所以ARP以及后续我们要讲的RARP协议都是链路层协议。
但是我们也不要忘了,IP层进行路由选择的时候,选择路径的本质就是选择下一跳的MAC地址,IP层需要将选好的MAC地址也告诉MAC帧协议,让MAC帧协议进行传送,所以,IP层也是需要知道对方的MAC地址的,而IP层需要知道对方的MAC地址才会交付给MAC层,那么就意味着,ARP请求的发起者是IP层或者说IP协议,而不是MAC协议。
ARP请求/应答能够直接发送到局域网中吗? 显然是不能的,发送到局域网中的必须是数据帧,或者说是MAC帧。
所以ARP请求/应答还需要经过MAC帧协议的封装。同时,也意味 ARP/RARP协议都是工作在MAC协议之上的。 那么学到这里,我们也就能发现一件事,在MAC帧协议中解包之后,进行分用的时候,并不一定要交给IP层,也可以是交付给链路层的ARP/RARP协议。
这是很正常的,说明ARP/RARP都只是一种链路层服务。 就像我们后续也会学习一些网络层服务,报文最终也就是交给网络层的服务协议,而不需要交给传输层。
那么请求时的广播与应答时的一对一通信的区别是什么呢?
广播的时候,局域网内所有的主机,链路层收到数据帧之后,MAC协议进行解包,数据帧的目的MAC地址目前还不明确,所以都不会或者说不能丢弃,而是交给ARP/RARP协议进行处理,有ARP/RARP来判断是否是发给字节的。 而进行应答时一对一通信,由于数据帧的目的MAC地址明确,所以只要目的MAC地址不是自己的MAC地址,在MAC协议就进行丢弃了,而如果是发给自己的应答,那么也是交给ARP协议继续处理,提取出想要的MAC地址,同时建立目的IP和MAC的映射关系。
那么回到协议报头,OP字段是用来干什么的?
我们上面有意无意在说明,ARP报文是有两种的,ARP请求和ARP应答,同时,一个主机可能会同时收到ARP请求和ARP应答,ARP协议 对于请求和应答的处理 其实是不同的,所以就必须要有一个字段来标识这个ARP报文是请求还是应答。 所以OP就是用来标识ARP报文的类型的,如果是ARP请求,OP字段的值就是1 ,如果是ARP应答,那么OP字段的值就是2。
而接收方MAC地址怎么填呢? 以及后续将ARP请求报文封装成MAC帧的时候,目的MAC地址怎么填呢?
ARP请求是要在局域网内广播的,所以他无需指明或者无法指明具体的目的MAC地址,这时候,目的MAC地址填的是 0xFFFFFF ,也就是全1。当MAC地址填为全1的时候,就意味着这个报文是要在局域网内广播的,相当于我们使用shell命令行时的 * ,通配符,所有的主机的MAC协议都会认为MAC帧是发给他的,不会丢弃。
那么ARP的详细过程就应该是这样的:
1 报文到达目的网络的路由器,但是只知道目的IP地址,在路由器中无法查到目的IP对应的MAC地址
2 IP协议发起ARP请求,相当于调用一个函数,然后让ARP协议去完成一次请求与应答
3 ARP协议构建ARP请求,目的MAC地址填为0xFFFFFF
4 ARP将请求交给MAC协议封装成MAC帧,MAC协议的报头中目的MAC地址也是0xFFFFFF,表示广播消息
5 局域网内所有的主机的链路层都会收到这个ARP封装成的MAC帧。 由于目的MAC地址为0xFFFFFF,所有的主机的MAC协议都不会丢弃,而是将ARP请求交给自己的ARP协议。
那么ARP协议怎么处理MAC层给的ARP报文呢?
首先,ARP协议事先也不知道这个ARP报文是请求还是应答,所以会先看OP字段。 如果是1,说明是请求报文,然后再去看接收方IP地址,如果不是自己的IP地址,这时候会丢弃。 如果是,那么就会进行ARP应答。
6 目的主机构建ARP应答,并交给链路层进行封装,一对一发送给请求方主机。目的MAC地址填为请求方的MAC地址
7 局域网内所有的主机的链路层都收到这个ARP应答,但是除了请求方主机之外,其他的主机的MAC协议检查到目的MAC地址不是0xFFFFFF且目的MAC地址不是自己的时候,丢弃这个数据帧。 而请求方主机的MAC协议收到这个MAC帧之后,解包并将有效载荷(ARP应答)交给ARP协议处理。
8 请求方ARP协议拿到ARP报文之后,首先还是提取OP字段判断是请求还是应答。 如果是应答, 那么这时候会直接提取ARP应答中发送方IP和发送方MAC地址。 提取出来之后,就为网络层维护这一对IP和MAC地址的映射关系
9 网络层知道对方的MAC地址,将报文交给MAC层进行封装发送给目的主机。
关于ARP:
a. ARP只会发生在入口路由器与目的主机之间吗?
并不是。只要在同一个局域网内,都有可能会发生ARP 。比如同一个局域网内的主机与主机之间,以及同一个局域网内的路由器与路由器之间,当然也可以是路由器与主机之间。
在我们进行数据的转发过程中,ARP可能一直在发生,只是我们的源主机已经不关心了。
b. 局域网一般不大,那么是不是只要知道局域网的子网掩码,就能将局域网中所有的主机的IP和MAC的映射关系构建出来?
当然。如果你想,主机或者路由器在知道子网掩码以及被分配一个IP地址之后,他可以粗暴的该网络号可能存在的所有的主机都列举出来,发生ARP请求,这样如果该IP被分配了,那么就可以获取到对方的MAC地址,如果没有被分配也没关系。
那么还有一个问题就是:每一次发送给该主机的报文到了这个入口路由器之后都需要ARP获取MAC地址吗?
并不是。 ARP获取到对方的IP和MAC之后,映射关系会被短暂缓存起来,要注意ARP缓存 是暂时的,有一定时效,同时后续也可能被丢弃。
缓存的映射关系会定期被清理,为什么呢?
因为网络中IP地址是不断在发生变化的。可能上一个使用该IP的主机下线了,另一台主机被分配了这个IP地址,那么就需要更新映射关系,不能一直用一个。 同时,也可以直接把网卡换了,改变MAC地址。
在Linux中查看缓存的命令是
arp 或者我们也可以带上 -a 选项
arp收到应答的时候,会以最新的结果为准,也就是说,如果arp缓存中已经存在这次ARP应答的IP和MAC地址的映射关系了,但是又收到了一个新的映射关系,这时候就会更新ARP缓存。
就比如我们的网络情况很差,发送方在发送一个arp请求之后,迟迟没等到回复,那么就会再次发送arp请求,如果这两个报文都到达对方了,那么最终就会收到两个arp应答。
如果局域网中有一台主机,他伪造了一个 arp 应答,把自己的MAC和局域网内所有的IP都关联一遍发给某一个主机,那么对方就会更新arp缓存,同时,在对方的ARP缓存中,所有的IP都对应的MAC地址都变成了这个恶意主机。 那么更新完arp缓存之后,目标主机发送到的所有数据都发送给了恶意主机。 同理,该主机也可以把路由器的arp缓存也用该方法更新了,那么最终到达这个局域网的所有数据也都会发送给恶意主机。
虽然这种情况下,恶意主机可以将收到的报文按照它原本的方向再发送出去,路由器和目标
主机都不会察觉到异样,但是所有的报文却都被这个恶意主机截取了。 这就叫arp欺骗,成为中间人。
所以这就愈加体现了https中证书以及加密的重要性,经过加密的话,就算被中间人截取到报文,也无法修改内容。
还有一种关于ARP的做法就是:
利用arp更新的机制,你可以伪造应答,将局域网所有可以存在的IP都映射一个不在此局域网中或者无效的MAC地址,将这些arp应答发送给一个目标主机,那么该主机未来发送的所有的报文都不会被局域网内任何一个主机接收,也就相当于掉网了。 或者最简单的方法就是只更新对应主机的路由器的IP和MAC的映射关系,把这一条映射的MAC更新了,那么他就只能在局域网内通信,而无法跨局域网通信,这也是一种方法,且更简单快速。 你也可以设计一个软件,专门用来干这事,硬编码arp应答中除目的IP之外的的字段,IP地址可以由用户指定,那么未来你想攻击局域网内的某台主机,只需要将他的IP地址填充到对应的字段就行了。 当然,我们不推荐这么玩。
RARP
有没有可能,我们只知道对方的MAC地址,而不知道对方的IP地址呢? 是有可能的,比如我们是网络管理员,配置了一个路由器,构建了一个局域网,但是数据转发不出去,这时候我们需要检查一下路由器的IP地址是不是我们曾经给他配置的IP地址。而路由器的MAC地址我们是知道的,这时候我们就需要一种功能:已知对方MAC地址,获取对方IP地址。
而完成这个功能的协议就叫做 RARP ,R就是reverse ,RARP 就是逆地址解析协议。
RARP的过程也很简单,因为我们是知道对方的MAC地址的,所以发送RARP请求的时候,也不需要广播,只需要一对一发送就行了。应答的时候就更不用说了。
其他的一些特点基本和ARP类似,我们就不做重点了。
其他的网络服务或者协议
DNS
DNS就是Domain Name Systerm ,其实就是一个域名解析服务。我们所说的域名就是常用的 www.baidu.com , www.bilibili.com 这样的一个字符串,域名在我们日常的上网中是十分常用的,但是在网络通信中,其实是没有域名的,只认IP地址,而不认所谓的域名。但是IP地址用起来十分难记且不方便,不如域名,可以和网站或者服务的名字产生联系,十分好记,于是就有了一套域名解析服务。
域名最大的价值就是便于互联网的商业化,让用户用起来更加方便。
但是由于在网络通信中,只有IP的概念,所以就必须要有一套能够将域名转换为IP的方案或者说服务,这就是DNS。
最早的时候,是直接将域名和IP地址的映射关系直接在客户端本地构建一个配置文件,当我们要访问某个域名时,会从配置文件中读取对应的IP。
但是这种方案的最大的缺点就是,影响范围太小,只在一台主机或者说只在构建了对应的映射关系的主机上能用 。
所以后续就有了专门的组织把所有的域名和IP地址的对应关系保存起来,任何人都可以通过他来获取域名对应的IP地址。这套服务就叫做域名解析服务,是由域名相关的组织搭建的。
域名一旦存在,我们的浏览器或者其他的客户端在去访问域名的时候,首先会请求DNS,获得对应的IP之后,拿着IP去进行访问,所以,域名终究还是一种上层的服务,并没有不是在网络的底层搭建的,同时,域名也不是必需的,所以所谓的域名解析也没有在前面学习的网络协议栈中出现。
同时,显而易见,为了提高效率,我们并不需要每一次访问都需要去请求DNS服务,我们的本地一定会保存最近请求过的域名的信息,我们也可以叫做DNS缓存。
域名怎么看呢?我们就拿 www.baidu.com 来举例
域名的后缀或者说 .com ,叫做一级域名。
一级域名中一般表明的是域名的性质,比如 .com 一般就是商业公司 .org 一般是非营利组织,.edu 一般是学校等教育机构或组织,而.cn等这些就是国家域名,.cn表示的就是中国。
baidu 就是二级域名,一般代表公司名或者网站名 ,www 我们也可以看成是二级域名的一部分,是一种习惯用法。
而有的域名在最开始还有一个字段用来表示支持的协议类型。
开放性面试题:
在浏览器输入 url 之后,发生了什么事情(完整的过程)?
这个问题其实是开放性的,首先,最基本的就是站在应用层来看,比如 通过域名获取IP,通过IP发起连接服务器请求,连接建立的三次握手,HTTP请求和相应的构成以及各个字段,属性字段的含义,状态码的类型和含义等等。
其次,我们可以从整个协议栈的角度来看,就是去谈操作系统在连接建立和通信时的细节,可以着重讲解TCP的各种可靠性机制,比如 滑动窗口,应答机制,超时重传机制,TCP的各个字段,流量控制,拥塞控制,以及数据在协议栈的流动的过程,同时还可以谈一下 路由的过程,以及转发过程中的ARP等等知识点。
ICMP协议
我们学习网络的时候,一直在谈如何通过网络将数据送达,如果数据未送达该怎么办,可是没有谈及网络如果出现问题,如何去排查网络的问题。 比如有些网络的转发路线由于故障不可达,可是我们的TCP协议和IP协议都没有关于这方面的问题的排查的功能。
ICMP协议用于验证网络的联通性的。
简单来说,使用ICMP协议用于验证我们的IP报文能否到达对方主机。
如果无法到达,也会返回一个报文用于通知源主机,IP报文为何被丢弃。
所以,ICMP是基于IP协议的,但是他又没有涉及到传输层的功能,只关心能否到达对方主机,不关心到达对方的进程,所以不需要具备端口号等信息。 我们还是把ICMP称为网络层协议。
同时,ICMP协议也只适用于IPv4,如果是IPv6的话,就要使用ICMPv6协议。
TCP的重传策略是用于发现问题之后的补救策略,而ICMP则是用来排查故障原因的。
ICMP大概分文两类报文,一类是通知出错原因,如果未出错也会返回网络的健康状态。 第二类就是用于诊断查询。
ping
ping 命令是基于 ICMP 协议的。ping命令的原理很简单,就是通过构建不同生命周期的报文,其实就是依次把IP报文的 TTL 设置为1,2,3,4 ... ... ,同时通过ICMP的返回结果是否为出错的返回值,来判断当前主机到远端主机的跳数。
同时,ping命令是基于 ICMP 的,也就是说它也是一种网络层的服务,不需要传出层,但是我们应用层可以直接调用这个命令,我们可以理解为 ping 命令是跳过了传输层,直接在网络层使用ICMP服务的。 所以ping 命令无需端口号。
traceroute
traceroute 命令和ping 命令类似,也是基于 ICMP协议的,也是网络层的功能,应用层跳过传输层直接调用。 所以 traceroute 服务也没有端口号。
他的功能是获取从当前主机到目标主机的路由器以及中间经历的时间。
NAT技术
在网络层的讲解中,我们已经知道了如何从运营商的内网中,不断转发和替换源IP,最终到达运营商的出口路由器发送到公网中。 但是有一个问题我们没有解决:
响应报文如何从运营商的入口路由器送到我们的主机?
因为最终返回的响应的目的IP都是这个入口路由器的公网IP,怎么送到对应的内网中的主机呢?
这就需要用到NAPT转换表了。
首先,不管是怎么通信的,通信双方都是一个进程,有自己的端口号,主机也有IP,虽然这个IP有可能是私有IP,但是至少在局域网中能表示唯一性。
同时,报文在经过路由器之后,虽然源IP换成了路由器的WAN口IP,但是端口号还是保留的,如果而端口号如果在路由器中唯一的话,那么是不是就可以用路由器中唯一的端口号来标识这一台主机?
我们直接先说结论,
为了支持响应能正确转发回局域网内的主机,每一个进行NAT的路由器都会自动维护一个NATP表,NATP表中会保存IP报文的替换前的四元组(源IP,源PORT,目的IP,目的PORT)和替换后的四元组(源IP,源PORT,目的IP,目的PORT) ,这两个四元组之间互为键值,构建映射关系。
我们就拿一个只经过一次转换的NAT进行举例
因为实际进行通信的是两台主机的两个进程,那么有以下几种情况
事先说明:由于我们举的例子中都是访问同一个服务器的同一个服务进程,使用的目的IP和目的端口都是一样的,为了画图方便,我们在NATP表中就省略了,但是要注意,实际的NATP表中是会有 目的IP和目的PORT的。
1 客户端可能有多个进程会同时访问同一个服务器
我们假设A主机与服务器进程进行通信,A主机两个进程,使用 8080和8081端口,那么这两个报文是怎么发送到公网以及路由器中的 NAPT 表中保存的是什么呢?
首先,第一个路由器也就是家庭局域网的路由器在转发两个保温之前,要进行NAT,把源IP进行替换,同时会在NATP表中保存这次的替换前的 addr(IP:PORT) 和 替换后的 addr 。相当于建立了一个映射关系。 然后路由器在转发给上一级的路由器。
而服务器在收到这两个请求之后,也会构建两个响应返回给我们的运营商的入口路由器。
而入口路由器会根据NATP表中的映射关系,找到之前出去的时候替换的那一个条目,将目的addr替换。
入口路由器再替换完之后,按照替换之后的目的IP转发,转发到下一级路由器之后,路由器会接着进行NAT替换
最终,在主机直接相连的路由器进行完NAT替换之后,服务器的响应就回到的我们的主机,同时,也回到了指定的端口号,交给了指定的进程。
我们只要知道了上面的几个图所描述的替换过程,后续的几种情况其实就很简单了。
2 同一局域网内的两个主机同时访问一个服务器,使用不同的端口号
这时候其实和第一种情况是一样的,只不过这次NATP表中,两个条目的左列信息 IP 和PORT都不同,那么对应的右列的信息的IP还是相同的,也就是路由器的WAN 口IP,而PORT还是用的报文原始的PORT,因为没有冲突。
3 同一局域网内的两个主机同时访问一个服务器,使用相同的端口号
这种情况下,其实我们的问题并不是NAT的过程,而是因为替换之后,端口号冲突了,这时候要怎么办?
其实很简单,在进行NAT替换之前,会查看路由器的端口号是否已经被使用了,如果已经被使用了,那么就线性搜索一个未使用的端口号来构建映射关系。
其实路由器这里的端口号我们不必当成真正的端口号,只是和IP一起组成一个二元组,表征在路由器中发出的消息的唯一性。我们就可以理解为一个数值就行了,没必要把他跟传输层的端口号看成是一样的。
在这种情况下,其实也很简单,无非就是线性向后找一个空闲的port值,只要表征唯一性就行了。
后续的过程其实还是跟前面的第一种情况一致。
端口号冲突的报文谁使用原始的端口号完全取决于报文到达的先后顺序,因为我们说了,路由器这里的端口号其实并不是传输层的端口号,只是用来和IP一起,表示发送方进程的唯一性的。
4 不同局域网的不同IP的主机使用不同端口号访问同一服务器进程
5 不同局域网的相同IP的主机使用不同端口号访问同一服务器进程
其实不同局域网内的相同IP的主机,在经过第一个路由器之后就已经和这个相同的IP无关了,这时候其实也和情况4一样。
6 不同局域网的不同IP的主机使用相同端口号访问同一服务器进程
这种情况我们可以将情况2和情况4结合起来,在入口或者说出口路由器中未冲突的端口号进行了替换。
这里我们可能会有一个疑问,按照上面的例子,我们好像只需要保存源IP和源PORT的二元组作为映射关系就行了啊,怎么还要保存目的IP和目的PORT,用四元组来标识唯一性呢?
其实也很好理解,因为一个主机上的一个进程可以访问不同的服务器,这时候也是需要维护这个关系的。
NAT技术的缺陷:
1 只能从内部往外部进行转发的时候才能建立连接,无法从外部主动与内部的主机建立连接。
2 NATP表的生成和销毁都需要成本
3 一旦NAT设备异常或者NATP表出现异常,这时候所有的TCP连接都会断开。(因为后续TCP保活报文无法发送到目的主机,这时候就会认为客户端异常,强制断开连接)
NAT穿透
也叫内网穿透,其实就是利用NAT和NATP表,把你的机器暴露在公网中,我们可以这样理解 :首先我们需要一个公网服务器,然后我们的主机不断向该公网服务器发送消息,保证NATP表中我们的主机和这个公网服务器的映射关系一直存在,而公网服务器收到的消息后续都转发给我们的主机做处理。 所以最终我们的主机就充当了公网服务器的功能。
但是我们可能会有一个疑问,为什么都有一个公网服务器了,还需要将请求通过内网穿透转发给在内网的服务主机呢?
其实这个公网服务器相当于提供了一层防护或者认证,只有通过这个外部的公网服务器的认证之后的请求,才会交给真正的服务器也就是在内网中的服务器继续处理。
代理服务器
前面学习的带有NAT技术的路由器,我们是不是可以换一种理解:
我们将请求交给了NAT路由器,然后由路由器代我们去访问服务器,最终路由器会将服务器的结果返回给我们的客户端。
而代理服务器和这样的有NAT的路由器有一点像,客户端要访问服务器的时候,是向代理服务器发送请求,由代理服务器将请求发送给真正处理的服务器,而服务器只会将结果返回给代理服务器,由代理服务器将结果再返回给客户端。
客户端在访问的时候,其实是看不到真正的服务器的,他只能将请求交给代理服务器。
而代理又分为两种:
1 反向代理 :客户端只要发送了请求,代理服务器都会将请求发给服务器,然后将服务器的返回的结果再返回给客户端,代理服务器不关心这个结果。
我们上面所画的图其实就是反向代理的图。
2 正向代理:客户端第一次发送请求的时候,代理服务器还是会将这个请求转发给服务器,然后服务器返回结果给代理服务器。 但是这时候代理服务器会将这个结果缓存一份,同时将结果返回给客户端。 后续如果有客户端也要请求这份资源,代理服务器就不会将这个相同的请求转发给服务器,而是直接将自己缓存的资源返回给客户端。
站在上帝视角,资源离客户端更近的就是正向代理,也就是资源可能在代理服务器中。 而资源每次只从服务器中来,就叫做反向代理。
那么代理服务器有什么优势呢?
首先,我们可以将代理服务器暴露在公网中,而把真正的服务器放在内网中,这样一来,服务器能够更加安全,能够通过代理服务器筛选一些非法的请求。
其次,站在公司的层面,如果流量大,一台服务器肯定是顶不住的,如果我们不使用代理服务器的话,就意味着,公司需要多台服务器都暴露在公网中,这时候安全风险肯定是增加了的,同时,公网IP是不足的,意味着我们需要增加更多的成本在IP上,但是我们不考虑这个,每个服务器都必须有一个公网IP,那么这时候客户端的请求发给哪一个服务器就取决于报文中的目的IP了,无法很好的进行负载均衡。如果我们的客户端的请求全部都是访问其中一台服务器的,那么我们搭建多台服务器的初衷就无法满足了,最终还是相当于只有一台服务器,其他服务器占用了资源,浪费了成本,却没有创造价值。
而如果我们使用代理服务器的方案,只将代理服务器暴露在公网中,而将服务器都放在代理服务器所搭建的内网中,这在增加了安全性的同时,还减少了所需公网IP的数量。 这时候,所有的客户端要访问只能访问这个代理服务器,而代理服务器则可以将客户端的请求分发给内网中的服务器。
同时为了提高效率以及增加服务集群的承载上限,我们的代理服务器需要有一定的策略,将收到的大量的客户端请求均衡的转发给集群中的各个服务器。 这样一来,就不会出现所有的请求都交给一个服务器处理的情况,大大增加了服务器集群的负载能力。 这就叫做负载均衡。
这个代理服务器一般要求配置很高,因为他需要有能力同时接收大量的客户端请求,同时根据策略转发给内网的服务器。
同时,由于需要接受大量的客户端请求,所以一般这种代理服务器都是反向代理服务器,尽可能地把性能都用在分发大量客户端请求上,而不需要将性能用在缓存资源上。 在这些服务器上一般使用类似于 Nginx 之类地反向代理服务的软件。
同时,我们也能注意到,结果从服务器返回给客户端也可以有两种做法,一种是服务器将结果返回给代理服务器,而代理服务器再将结果返回给客户端。 其次就是我们可以直接通过NAT技术,直接从内网将结果返回给客户端。 按照逻辑来讲,第二种做法肯定是效率比较高的,因为不需要代理服务器再做其他的事。
当然,其实客户端和代理服务器之间也不是直接相连的,在代理服务器的前面可能还有防火墙等设备或者软件。
在我们的学校场景中,我们使用校园网的时候,有时候我们会发现有些网页是打不开的,这是因为在我们的客户端和服务器或者服务器的代理服务器之间还存在这一个代理服务器,我们的请求其实都是交由学校的代理服务器去进行访问的,那么于此同时,我们的请求就有可能被学校的服务器拦截。 同时有的学校还会在代理服务器提供缓存功能,也就是搞成正向代理服务器,这时候如果我们申请相同的资源,就是直接在内网中将缓存的内容返回给你,速度比较快。
NAT和代理服务器的区别?
1 首先从功能上来说,NAT是如今路由器上面必须采用的策略,解决的是IP不足的问题,和硬件联系的。而代理服务器则是更贴近服务器也就是具体的应用,也就是更贴近应用。
2 从底层实现来说,NAT是工作在网络层(当然也可以说是工作在传输层,因为前面我们也知道NAT能看到端口号),对IP(或者以及端口)进行替换,而代理服务器则是工作在应用层的。
3 从使用范围来将,NAT一般在局域网的出口路由器上部署,而代理服务器可以在内网,可以在公网,也可以跨网络。
4 从部署位置上来看,NAT一般集成在防火墙,路由器等硬件设备上,而代理服务器则是一个软件程序,需要部署在服务器上。
我们上面所说的使用代理服务器进行负载均衡,将大量请求分发给内网中的服务器,我们可以理解为他是局域网中的代理。
而广域网中的代理其实最常见的就是我们所说的翻墙(后面我们简称为 FQ )。
FQ的原理:
我们普通用户,客户端都是在运营商的内网环境的,那么要访问公网的服务器就一定需要经过运营商。如果我们要访问外网,正常来说,检测到你要访问的网址是国外的网址,在运营商的路由器或者服务器就直接给你拦截了,不会让你访问。
同时,在我们国内也是有一些对接世界的窗口的,比如香港地区,澳门地区等,他们是能够直接访问外网的,那么也就意味着,搭建在这些地区的服务器也是能够范围外网的,于此同时,这些服务器还是假设在我们国内,所以如果我们只是访问国内的服务器,那么运营商一般来说是不会拦截的。
说到这里结果已经呼之欲出了。 如果我们想要FQ,那么首先就需要有一台在国内,同时能够访问外网的服务器,我们可以当成云服务器来用。同时,在这个服务器上要部署代理服务器服务。 那么未来我们可以使用特定的一些客户端,当我们进行访问的时候,假设是https协议进行访问,这个客户端和服务器的通信是使用https的,那么未来我们使用这个客户端进行外网的访问,这个访问外网的报文请求并不是直接通过运营商发给外网。 而是在客户端会做一些处理,首先,将我们的原始的访问外网的https/http 报文进行加密,然后作为一个https报文的有效载荷,将这个包含有加密过的https/http请求的https报文发给我们的代理服务器,而在代理服务器上的逻辑就是将收到的https报文进行解密,解密出来的有效载荷就是我们的访问外网的请求,然后将这个请求发出去,这个服务器是能够访问外网的,那么最终外网的服务器将请求的资源发给了我们的代理服务器,最终代理服务器将返回的资源还是经过https进行加密,然后作为一个 https 的响应报文的有效载荷,返回给我们的客户端,而我们的客户端将收到的https相应进行解包之后,将有效载荷进行解密,这样就获得了我们想要的资源。
当然这样的配套的客户端和服务器都是需要我们付费的,这也很好理解。
当然,我们还是不推荐FQ 这种行为的,我们仅仅是从原理的角度来解析,不推荐应用。
以上就是链路层的理解,以及对网络层中我们没有讲完的一些知识点进行补充,因为有些知识点只有将网络的整体的转发的框架掌握之后我们才能更好理解,所以就放在了这里。