网络包是如何在主机以及网络上进行流转的

195 阅读13分钟

包流转路径

发包流程,一个ip网络包发送前,需要通过arp协议去获取mac地址(如果本地没有arp缓存的话),获取到mac地址后,内核会封装网络ip包对网络包进行发送。

包如何在局域网传播,如何传递到其他网络?

image.png

这是比较著名的OSI模型

  • 应用层
    http/https协议,websocket协议,dhcp协议、websocket协议、p2p协议
  • 传输层
    tcp和udp
  • 网络层(在这一层进行ip网络包发送)
    ip协议和icmp协议(ping命令就是发送的icmp协议)。
    一个网络层的ip网络包发送之前,会有一个arp的请求发送出去。
  • 数据链路层
    这一层就涉及到arp协议,术语叫帧
  • 物理层
    就像网线之类的

要理解一个网络包的发送以及arp的发送,首先理解的是一个包是如何在主机上进行流转的,一个网络数据包是在一台一台主机上接力传递下去的,在最终到达目的之后,又是从目的地主机通过一系列的中间设备或一系列的主机,又传回到最开始的主机,那网络包是如何寻找它的下一台主机呢?

下一台主机即下一跳地址/目的地,术语是nexthop。

在发送ip包之前,会把发送的目的ip与本地路由表去进行比较,看ip包是从哪个网卡出去,通过ip去寻找路由规则rule,通过路由规则rule去特定的路由表去寻找路由策略。

在主机上一些路由规则,每一个路由规则会有它自己的一些路由策略,最终网络包就是根据这些路由策略选择具体从哪个网卡出去。

主机上的路由规则

image.png

前面的数字代表优先级,0代表优先级最高;local、main表示路由表的名称,一个路由表是包含很多路由策略的

路由策略ip route

image.png

  • 192.168.168.0/24 为要发往的目的网段
  • dev ens33 proto kernel 由网卡ens33发送,ens33是由内核安装的
  • 192.168.168.156 发送出去的ip地址

192.168.168.0/24这个网段的网络包会经由ens33这个网卡发送出去,ip是192.168.168.156。

如果本地没有路由,则默认发往网关的地址,不论是发往本地路由,还是网关路由,发送之前都会通过arp协议获取网关或者对应路由网卡的mac地址。

mac地址得到之后,就将数据包发往nexthop站点。

看下local这个表的路由策略

ip route list table local

image.png

是对本机上面一些网卡的原始地址去进行路由,像127.0.0.1它会由本地的一个 lo网卡出去(lo是本地回环地址),像192.168是一个局域网的地址,它是由物理网卡出去的。

而外部路由是通过ip route去查看的,ip route其实是查看main这个路由表。

image.png

这里default是代表如果你的网络包所要发往的目的地址找不到合适的路由规则出去,那它默认就会由enp33这个网卡出去,并且下一跳地址是192.168.168.2,出去的这个网络包,没有原始地址的话,默认的原始ip是当前主机的静态ip 192.168.168.156,这里的下一跳地址一般是网关地址,把网络包发送了给网关。

网关

网关是联通不同局域网的一个网络设备,是一个路由器,能够把不同的局域网进行关联,让你的网络包从一个局域网发送到另一个局域网。

无论是发送到本地网卡还是网关的网卡,它都要先获取本地网卡或网络网卡的mac地址,要先发送arp协议才可以得到mac地址,有了mac地址之后,才会发送ip网络包。

怎么看目的ip和本地网卡是不是同一个局域网(同一个网段)?

192.168.168.0/24 其中的/24代表ip转换成二进制之后,前24位代表一个网络段,后24位代表真正的ip地址序号,如果2个ip的网络段相同的话,那就属于同一个网络,能够在局域网之间发送arp。

比如同一个网络当中,发送192.168.168.156 ip网络包,这个主机内核检测到你发往的这个目的地址是属于192.168.168.0/24,24这个网段,那它就会从ens33网卡出去。

截止目前,说完了ip网络包是如何在主机上进行路由发送的,之后具体看下arp协议

arp协议

arp协议主要获取网络包的目的地址,目的地址代表一个网卡的mac地址。只能在局域网之间传播,也就是说如果你的一个目的ip和当前一个ip不在同一个局域网内,那你发送的arp协议,只会是最终获取到一个默认网关的地址,先把你的ip网络包传给网关,然后由网关再根据你的ip转发到网关层面的另一个网关上,然后通过另外一个网卡,再继续发送arp协议去获取他的下一跳mac地址。

arp协议发送请求和应答很简单,只需要看同一个局域网内,你的一个目的ip是不是我要寻找的目的ip。

第一次发送request请求的时候,会在整个局域网去广播它的请求,然后如果目的ip和你主机上面收到请求的那个ip包的主机是一样的,那这个主机就会发送arp响应,否则的话,就会丢掉arp协议的这个包。

响应之后呢,发送arp协议的这台主机,就获取到了对应的mac地址,然后内核就会继续发送ip网络包过去。

arp发送的链路层的包是不能跨网段的。

在一个局域网里通常会存在交换机,交换机的作用是将局域网里的机器通过网线连接起来,当一个数据包到达交换机的时候,交换机会通过包的目的mac地址看是单播还是广播,连上了端口就能发送出去。

image.png

在了解了arp协议和网络包的路由策略是如何转发的之后,我们来看下在同一主机里docker容器里面和容器与容器之间是怎么互相通信的。

路由器

路由器转发的是ip网络包,路由器一般也具有交换机的功能。之前提到如果本地没有对应网络包的路由就会将包发默认网关,一般配置的默认网关的ip所在的机器具有路由功能。将ip网络包收到以后,如果目标ip不是在接收网卡的网段,路由器就会去寻找自己机器上的其他网卡,看是否具有相同网段的网卡,有的话就从那个网卡把包发出去,否则就走它的默认网关出去。

在docker里面是由一个bridge网桥作为各个容器的默认网关,然后通过这个网桥去连接了各个容器。各个容器之间是通过veth这个网络设备去进行相关联的。

命名空间

docker里面每个容器都有自己的网络命名空间,这也是linux内核所提供的功能,可以通过ip route这个工具包看下,

image.png

目前这个机器上是没有网络命名空间的,可以通过add命令增加一个

image.png

有了网络命名空间之后,在这个网络命名空间以内,有关于网络的一些设备、路由、arp缓存、域名等,都是独占一份的,这个命名空间内和你的主机(主机是一个默认的网络空间)都是互相隔离的。

网桥可以把它类比为现实世界当中的一个交换机,交换机只认mac地址,它只会广播你的请求,它不关心你的ip具体是多少,也就是说,交换机只能保障你的包在局域网之间传输。

你从交换机上面一个端口发送了包,它会广播到交换机的其他端口上去寻找mac地址,然后得到mac地址之后,它会建立端口,还有mac地址之间的一个映射关系,那下一次再发送包请求的时候,它会匹配这个映射关系,从指定端口出去了。

bridge

默认一个虚拟机上面已经有了一个docker0网桥。bridge是linux上的交换机,linux开启ip forward功能后,并赋予bridge一个ip,让bridge具有路由的功能,转发不同网段的网络包。

  • 安装btctl

    yum install bridge-utils

  • 查看网桥
    brctl show

    image.png

  • 添加网桥
    brctl addbr 网桥名

网桥是一个交换机,在它上面可以连接很多网络设备。交换机是没有ip地址的,但网桥可以拥有自己的ip地址。

image.png

在主机上面,docker0这个网桥,它有一个172.17.0.1的ip地址,网段是前16位172.17.0。

为什么要有这个ip地址

因为你如果没有这个ip地址,你只能保证容器与容器之间在172.17.0这个网段内通信,即容器的一个局域网内可以通信,你不能把你的ip网络包去传输到局域网外面去,你不能传输给主机上面的其他网卡,你所创建的容器将不能和主机去进行通信。

因为交换机是一个二层转发设备,它只认mac地址,它不会将不同区域的一个ip网络包发送出去,但是给这个网卡,给这个docker0这个网络设备去填上了一个ip以后,那这个设备将会和内核协议栈相连,让发送到它的网络包 将会到达内核协议栈,内核协议栈发现这个网络包,如果目的地址不是这个网桥所属的这个网段的话 ,它会转发到主机上面的其他网卡上,由其他网卡进行发送,这也就是我们为什么要在去设一个ip的原因,是为了让网桥所关联的网络命名空间能够和主机,能够和公网去进行通信。

veth-pair

linux上的一个网络设备,这个网络设备具有两个端点,数据从一个端口进入,必然从另一个端点流出,每个veth都可以被赋予ip地址,并参与三层网络路由的过程。

网络设备被赋予ip之后,那么它的一端就可以认为与协议栈相连。

veth设备是成对出现的,你创建一个veth设备,那它就会有两个网卡出现,并且这两个网卡的数据从一个端口进入的话,肯定会由另一个端口接收到,

  • 创建一个网络命名空间
    ip netns add test_veth
  • 增加一对网卡
    ip link add veth-test type veth peer name veth-test2
    再来看看网卡,发现多了2个网卡

image.png

不过它们的状态都属于down状态,并且目前是没有ip地址的。

  • 把其中一个网卡添加到test_veth这个网络命名空间里面
    ip link set veth-test2 netns test_veth
    添加之后,可以看到主机上的veth-test2这个网卡就不见了image.png因为主机其实也是一个默认的命名空间,这个命名空间和我创建的test-veth这个命名空间是隔离的

  • 查看test-veth命名空间上的网卡
    ip netns exec test_veth ip addr

    image.png可以看到刚才加进来的一个网卡。lo是本地默认的一个回环地址的网卡

  • 把这两个veth设备启动

    ip link set veth-test up

    image.png

    ip netns exec test_veth ip link set veth-test2 up

    image.png

  • 赋值成两个不同网段的ip

    ip addr add 10.11.13.3/24 dev veth-test

    image.png

    ip netns exec test_veth ip addr add 10.11.14.4/24 dev veth-test2

    image.png

在test-veth2上面这个网卡设备的ip我能够ping通呢?

在主机上去ping一下在test-veth2上面网卡设备的ip 10.11.14.3,image.png

发现是ping不通的,ping这个ip,发送网络ip包之前,会通过目的地址去寻找mac地址,但它又从哪个网卡把arp协议承载的包给发送出去呢?可以来看下当前的一个路由情况

image.png

10.11.4才是它的网络号,找不到对应路由网卡,它默认会从ens192的网卡出去,来到它的下个网关。这个ip地址是我在本地主机上面的一个ip地址,它从网关出去,肯定是找不回来了,那此时我就应该往我的主机上面去添加一个路由,

  • 添加路由
    ip route add 10.11.14.0/24 dev veth-test
    让我访问10.11.14这个网络段的时候,要从本地的一个设备出去,

image.png

这里已经添加成功了一条路由,如果是这个网络段,那我就要从veth-test这个设备出去,那它就会到达test_veth这个网络命名空间里面的veth-test2这个网络设备上。

再来ping一下,发现还是ping不通的,这时候可以分析一下现在包的流转情况,用tcpdump去抓一下包。首先来看下veth-test这个网络设备上的网络包

  • yum install tcpdump

  • tcpdump -i veth-test -n

    image.png

可以看到它一直在发arp请求,有request标志的请求,说明是从这个网卡出去的包,并且网卡在等待一个arp响应,回应之后,它才会拥有对端ip地址对应的mac地址。

那来看下test-veth这个网络空间内的test-veth2这个网络设备接受包的情况

ip netns exec test_veth tcpdump -i veth-test2

image.png

可以看到它这里也是接收到了arp的包,但是为什么没有去对这个arp包进行回应呢?

它的ip也和所要请求的ip地址是吻合的,是14.3这个ip地址。我们可以去这个命名空间上面看一下它的一个路由,ip netns exec test_veth route -n

image.png

可以看到它只有一条路由是发往10.11.14这个网段,它才会路由到这个网卡,也就是说我们的arp的请求的包是到达了这个网络命名空间以内,但是它找不到回去的网卡,没有一个回去的路由策略,不会对这个arp包进行回应,那只要加上一个默认的路由,让它从veth-test2这个网卡回去,那之前的ping命令就能够通了。

ip netns exec test_veth ip route add default dev veth-test2

image.png

添加好了,默认的网关,默认是从veth-test2这个网卡路由出去,那这样就可以ping通了image.png