转自:http://blog.csdn.net/zztflyer/article/details/50441200
目录
[前言]
无论学习OpenStack或CloudStack,或是其他的云平台产品,网络都是举足轻重的概念和技术。但是网络知识本身就很庞大和复杂,学习起来非常困难,甚至不知道从何处下手。前段时间翻看OpenStack的文档,发现里面的文章写的网络基本入门知识非常浅显易懂,但是没有中文版本。好东西当然要分享,我便在闲暇时间将这篇文章翻译过来了,希望对渴望了解网络知识的人提供一点帮助。同时由于水平有限,里面的错误和疏漏之处也请大家批评指正。
原文地址:http://docs.openstack.org/mitaka/networking-guide/intro-basic-networking.html
以太网(Ethernet)
以太网是一种基于IEEE802.3标准的网络协议。大部分以有线接入通信的网卡(NICS)使用的都是以太网协议。
在OSI七层网络模型中,以太网位于第二层,即数据链路层。当我们讨论以太网时,会经常听到诸如本地网络,第二层,链路层,数据链路层等概念。
在以太网中,连接到网络中的主机之间通过交换数据帧通信。每一台主机使用MAC地址作为网络中的唯一标识。在OpenStack环境中,每一个虚拟机实例拥有一个单独的MAC地址,并且不同于计算节点的MAC地址。每个MAC地址的长度是48位,通常以十六进制的方式表示,如08:00:27:b9:88:74。每块网卡在出厂时就会被注入MAC地址,现在的网卡可以通过工具或者编程的方式修改MAC地址。在Linux系统中,可以通过IP命令获取一块网卡的MAC地址。
$ ip link show eth0 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether 00:0c:29:de:4b:31 brd ff:ff:ff:ff:ff:ff |
理论上说,可以将以太网理解为一个网络上所有主机连接的总线。最初的时候,以太网是用同轴电缆实现的,每一台主机通过接口连接到同轴电缆上。现代的以太网络不再采用这种方法了,而是将主机直接连接到一个叫做交换机的网络设备上。但是这个概念模型是有用的,在网络图中,包括OpenStack的Dashboard网络图,以太网就表现为一个单独的总线。我们会经常听到一个以太网网络被称为一个2层网段。
在同一个以太网网络中,这个网络上的每台主机都可以直接向其它的主机发送数据帧。以太网也支持广播,所以一台主机可以通过向一个特殊的MAC地址ff:ff:ff:ff:ff:ff发送的方式将数据帧发送给该网络上的所有主机。ARP和DHCP是使用以太网广播的两个典型的协议。因为以太网支持广播,有时我们会把一个以太网网络称为一个广播域。
当一个网卡收到一个以太网数据帧时,这个网卡缺省会检查目的MAC地址是否与网卡的MAC地址匹配(或是广播地址),如果不匹配就会丢弃这个以太网数据帧。对于一个计算主机,这种行为是不期望的,因为这个数据帧可能是发送给计算节点上一个实例的。网卡可以配置为混杂模式,即使MAC地址不匹配,也可以将所有的以太网数据帧发送给操作系统。因此,计算节点应当将特定的网卡配置为混杂模式。
正如之前提到的,现代以太网使用交换机作为网络上主机的互联设备。一个交换机是一个有很多个端口的网络硬件盒子,以太网通过交换机将以太网数据帧从一个主机转发到连接到交换机上的另一个主机。当主机第一次通过交换机发送数据帧时,交换机不知道哪个MAC地址连接到了哪个端口。如果一个以太网数据帧指向一个的MAC地址,交换机会将数据帧广播到所有的端口。交换机端口通过观察数据流学习哪个MAC地址关联到哪个端口。一旦这个端口知道了MAC地址和端口的关联关系,它可以直接将以太网数据帧发送到正确的端口而不必再广播了。交换机维护MAC地址和交换机端口的对应表,这个表称之为一个转发表或转发信息库(FIB)。交换机之间可以通过daisy-chained(菊花链)的方式连接到一起,这样做的结果就是主机和交换机就像连接到了一个网络上。
虚拟局域网(VLANs)
VLAN是一种虚拟网络技术,它可以将一台物理交换机划分成多个逻辑交换机,使得每个逻辑交换机同独立的物理交换机一样。这样,连接到同一台物理交换机上的两台主机如果属于不同的VLAN,则它们之间彼此看不到对方的网络流量。OpenStack利用VLAN的这个特性隔离不同租户的网络通信,即使不同租户的实例在同一台计算主机上运行。每一个VLAN都有一个关联的ID号,VLAN ID是1~4095中的一个数字。我们说“VLAN 15”就表明这个VLAN关联的ID是15。
要理解VLAN是如何工作的,可以参照VLAN在传统IT环境下是如何应用的,物理主机连接到物理交换机,不引入虚拟化。想象一个场景,你只有一台物理交换机,但是想创建3个网络。网络管理员会选择3个VLAN ID,例如10,11,12,并且将VLAN ID与交换机上的端口关联起来。例如,交换机端口2与VLAN10关联,端口3与VLAN 11关联,等等。当一个端口配置一个特定的VLAN,这个端口就被称为访问端口(Access口)。这台交换机相应就保证了VLAN之间网络流量是隔离的。
我们再考虑一个场景,第一台交换机的所有端口都已经被使用了,单位又买了第二台交换机并且与第一台交换机连接起来以扩展交换机端口的数量。第二台交换机也支持VLAN 10,11,12。设想连接到第一台交换机配置为VLAN 10端口的主机A发送一个以太网数据帧给主机B,而主机B连接到了第二台交换机配置为VLAN 10的端口。当第一台交换机向第二台交换机转发转发数据帧时,它必须标记数据帧与VLAN ID10关联。
如果两台交换机连接在了一起,并且它们都配置了VLAN,连接两台交换机的端口必须配置为允许任何VLAN的数据帧转发到另外一台交换机上。此外,发送数据的交换机必须为每个以太网数据帧打VLAN ID标签,这样接收交换机可以保证只有与VLAN匹配的主机接受到数据帧。当一个交换机端口被配置为通过所有VLAN的数据帧并且将这些数据帧打上VLAN ID标签时,这个端口就被称为Trunk口。IEEE 802.1Q网络标准描述了应用trunk时,VLAN标签如何在以太网数据帧编码的。
需要注意的是,在OpenStack云中,如果你使用在物理交换机上划分VLAN的方式实现多租户隔离,必须保证所有的交换机端口都配置为trunk端口。
确保在云中选择使用没有被使用的VLAN范围。例如,预计你的云将会支持最大100个项目,需要选择一个大于100的VLAN范围,比如VLAN 200-299。OpenStack和所有处理租户网络的物理网络基础架构必须支持这个VLAN范围。
打trunk是为了连接不同的交换机。每个trunk使用一个标签标识使用了哪个VLAN。这样就保证了同一VLAN的交换机可以通信。
子网和地址解析协议(Subnets and ARP¶)
网卡使用MAC地址标识网络中的主机,然而TCP/IP应用使用IP地址。地址解析协议(ARP)通过将IP地址转换为MAC地址的方式将二者之间的缝隙连接起来。
IP地址分为网络号和主机号两个部分。如果两个主机有共同的网络号,则它们处于同一个子网中。回忆一下两台主机只有在同一个本地网络中才能通过以太网直接通信。ARP认为在同一个子网内的主机都在同一个本地网络上。网络管理员在为主机分配IP地址和子网掩码时必须注意任意两台同一子网的主机必须在相同的本地网络上,否则ARP就不能正常解析IP地址。
通过IP地址的子网掩码可以计算出IP地址的网络号。子网掩码表示32位的IP地址中有多少位标记网络号。
子网掩码的两种表示方法:
● 点分符号法
● 无类域间路由(CIDR)法
考虑一个IP地址192.168.1.5,它的前24位表示网络号。用点分方法的写法是255.255.255.0。CIDR表示法包含IP地址和子网掩码,这个IP可以写为192.168.1.5/24。
注意:
在OpenStack环境中创建CIDR子网不能包含多播地址或者本地环路地址。例如不支持使用224.0.0.0/16或者127.0.1.0/24创建子网。
有时我们想要表示一个子网,但是并不指定子网上具体的IP地址。一个通用的做法是把所有的主机标识设为0。例如,如果一个主机的IP地址是10.10.53.24/16,我们说这个子网是10.10.0.0/16。
要理解ARP是如何将IP地址转换为MAC地址的,考虑下面这个例子:假设主机A的IP地址是192.168.1.5/24,MAC地址是fc:99:47:49:d4:a0,并且主机A想要向主机B发送一个包,主机B的IP地址是192.168.1.7。注意,这两台主机的网络号是一样的,所以主机A可以直接向主机B发送数据帧。
第一次,主机A想要与主机B通信,目的MAC地址是不知道的。主机A向本地网络发起一个ARP请求。这个ARP请求发起一个如下的广播消息:TO:everybody(ff:ff:ff:ff:ff:ff)。我正在寻找一台IP地址为192.168.1.7的计算机。签名:MAC地址fc:99:47:49:d4:a0。
主机B做如下回应:
To: fc:99:47:49:d4:a0。我有IP地址192.168.1.7。签名: MAC address 54:78:1a:86:00:a5。
主机A向主机B发送以太网数据帧。
你可以使用arping命令手工发起一个ARP请求。例如,向IP地址172.16.28.2发送ARP请求:
$ arping 172.16.28.2 ARPING 172.16.28.2 from 172.16.28.241 eth0 Unicast reply from 172.16.28.2 [00:50:56:E6:47:5F] 0.818ms Unicast reply from 172.16.28.2 [00:50:56:E6:47:5F] 0.797ms Unicast reply from 172.16.28.2 [00:50:56:E6:47:5F] 0.757ms Sent 3 probes (1 broadcast(s)) Received 3 response(s) |
为了减少ARP请求的次数,操作系统维护了一个ARP缓存,包含了IP地址向MAC地址的映射。在Linux机器上,可以使用arp命令查看ARP缓存里的内容:
$ arp -n Address HWtype HWaddress Flags Mask Iface 172.16.28.2 ether 00:50:56:e6:47:5f C eth0 172.16.28.1 ether 00:50:56:c0:00:08 C eth0 |
DHCP¶
主机可使用动态主机配置协议(DHCP)自动获取网络IP地址。DHCP服务器负责将IP地址分发到网络上的主机,这些主机称为DHCP客户端。
DHCP客户端通过68端口向255.255.255.255这个地址的67端口发送一个UDP包来定位DHCP服务器。地址255.255.255.255为本地网络的广播地址:本地网络上的所有的主机都可以看到发送到这个地址的UDP包。然后,这些数据包不会转发到其他网络上。所以,DHCP服务器必须与DHCP客户端在同一个网络上,否则DHCP服务器就收不到来自客户端的广播包。DHCP服务器通过从67端口发送一个到客户端68端口UDP包来响应客户端的请求。客户端与服务器端的数据交换大体如下:
1. 客户端发送一个发现请求(我是一个在MAC地址08:00:27:b9:88:74上的客户端,我需要一个IP地址)
2. 服务器端提供服务(好的,08:00:27:b9:88:74,我为你提供了10.10.0.112这个IP地址)
3. 客户端发送一个请求(服务器10.10.0.131,我将会使用10.10.0.112这个IP地址)
4. 服务器发送一个确认(好的,08:00:27:b9:88:74,10.10.0.112这个IP地址是你的了)
OpenStack使用了一个叫做dnsmasq的第三方程序实现了DHCP服务器。Dnsmasq将DHCP的请求和应答写入到了syslog,我们可以在syslog中查看。
$ Apr 23 15:53:46 c100-1 dhcpd: DHCPDISCOVER from 08:00:27:b9:88:74 via eth2 Apr 23 15:53:46 c100-1 dhcpd: DHCPOFFER on 10.10.0.112 to 08:00:27:b9:88:74 via eth2 Apr 23 15:53:48 c100-1 dhcpd: DHCPREQUEST for 10.10.0.112 (10.10.0.131) from 08:00:27:b9:88:74 via eth2 Apr 23 15:53:48 c100-1 dhcpd: DHCPACK on 10.10.0.112 to 08:00:27:b9:88:74 via eth2 |
当排查一台实例无法通过网络访问时的问题时,可以通过检查这个日志来验证DHCP协议的四个步骤来帮助我们解决问题。
IP¶
互联网协议(IP)规定如何在两台连接在不同本地网络上的主机之间路由数据包。IP依赖称为路由或网关的特殊主机。一个路由器是一台连接到至少两个本地网络上的主机,并且可以将IP数据包从一个本地网络转发到另外的本地网络。一个路由器有多个IP地址:每一个IP地址对应与其连接的网络。
在OSI七层网络模型上,IP位于第三层,这一层被称为网络层。当我们讨论IP是,会经常听到诸如第三层,L3和网络层这样的词汇。
当一台主机向一个IP地址发送数据包时,它首先会查询自己的路由表,然后决定将数据包发送到本地网络的哪台主机上。一个路由表维护了这台主机所连接的本地网络的子网清单,以及这些本地网络上的路由器的清单。
在一台Linux机器上,下面的任何一条命令都可以显它的示路由表:
$ ip route show $ route –n $ netstat -rn |
下面时使用ip route show输出路由表的例子:
$ ip route show default via 10.0.2.2 dev eth0 10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 192.168.27.0/24 dev eth1 proto kernel scope link src 192.168.27.100 192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1 |
第一行的输出指定了默认路由的位置,当其他的路由规则都不匹配时由它作为一个有效的路由。连接在默认路由上的路由器(上面例子中的10.0.2.2)有时会作为默认的网关。DHCP服务器一般会将默认网关的IP地址以及客户端IP和子网掩码传输给客户端。
第二行输出表示在10.0.2.0/24子网中的IP处于与网卡eth0连接的本地网络上。
第三行输出表示在192.168.27.0/24子网中的IP处于与网卡eth1连接的本地网络上。
第四行输出表示在192.168.122/24子网中的IP处于与网卡virbr0连接的本地网络上。
Route –n 和netstat-rn命令的输出格式只有细微的差别。下面这个例子展示了使用这两个命令的输出格式:
$ route -n Kernel IP routing table Destination Gateway Genmask Flags MSS Window irtt Iface 0.0.0.0 10.0.2.2 0.0.0.0 UG 0 0 0 eth0 10.0.2.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 192.168.27.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1 192.168.122.0 0.0.0.0 255.255.255.0 U 0 0 0 virbr0 |
Ip route get命令输出目的IP地址的路由。下面的例子中,目的IP地址10.0.2.14在本地网络的eth0网卡上,可以直接发送:
$ ip route get 10.0.2.14 10.0.2.14 dev eth0 src 10.0.2.15 |
目的IP地址93.184.216.34没有在主机连接的任何本地网络上,将会转发到10.0.2.2这个缺省网关上。
一个IP包通过多个路由器跳转到达目的地址是通常发生的情况。在一个Linux主机上,traceroute和最近的mtr程序可以打印出一个IP包经过的每个路由器的IP地址。
TCP/UDP/ICMP¶
网络软件程序通过IP网络通信,它们需要使用IP层之上的协议。这些协议位于OSI模型的第四层,被称为传输层或第四层。可参考由互联网数字分配机构(IANA)维护的这个网页,这个网页列出了IP协议智商的协议列表和它们关联的协议号。
传输控制协议(TCP)是最常用的4层协议。TCP是一个面向连接的协议:它使用客户端-服务器端模型,客户端连接到服务器端,服务器端启动收到连接的应用程序。基于TCP的应用程序典型的交互过程如下:
1. 客户端向服务器端发起连接请求
2. 客户端羽服务器端交换数据
3. 客户端与服务器端断开连接
因为一个网络主机可能会有多个基于TCP的应用在运行,TCP使用一个叫做端口的地址模式来唯一标识基于TCP的应用。TCP端口的范围为1-65535,操作系统强制限制在同一时间主机上仅能有一个应用程序使用同一个端口。
一个TCP服务监听一个端口。例如,SSH服务通常监听22端口。一个客户端使用TCP连接到一个服务器,这个客户端必须同时知道服务器的IP地址和TCP端口。
TCP客户端程序的操作系统自动为客户端分配一个端口号。这个客户端将会保留这个端口号直至TCP连接中断,之后操作系统会回收这个端口号。这类端口称为临时端口。
IANA维护了很多基于TCP服务的端口号记录表,以及使用端口号的其他四层协议。注册一个TCP端口号是不必要的,但是注册一个端口号对于避免与其他服务冲突是有帮助的。请参考附录B:OpenStack配置指南的防火墙与默认端口部分查看部署一个OpenStack环境相关的不同服务使用的默认端口号。
写基于TCP程序最常用的软件编程接口是伯克利sockets,另外的称谓是BSD sockets 或简称sockets。Sockets API为TCP程序释放面向流的接口:从程序员的视角来,通过TCP连接发送数据类似于向一个文件写字节流。操作系统的TCP/IP实现将数据流分解组合成IP数据包。操作系统也负责重传丢弃的数据包,并且负责流量控制,从而保证传送的数据不会超出发送数据缓存、接收数据缓存和网络传输能力。最终,操作系统在接受端将数据包按照正确的顺序重组成数据流。因为TCP检测并重传丢失的数据包,所以它被称为可靠的协议。
用户数据报协议(UDP)是另一种四层协议,它是很多著名网络协议的基础。UDP是一个无连接的协议:两个通过UDP通信的应用程序在交换数据中前无需建立连接。UPD协议同时也是一个不可靠的协议。操作系统不会监测或重传丢失的UDP数据包。操作系统同样不保证接受到的数据包顺序与发送顺序一致。
UDP与TCP一样,使用端口的概念区分运行在同一个系统上的不同应用。需要注意的是操作系统对待UDP端口与TCP端口是分开的。例如一个程序使用TCP端口16543,另一个程序可以使用UDP端口16543。
与TCP一样,Socket API是写基于UDP程序的最常用的接口。Sockets API为UDP程序提供了一个面向消息的接口:一个程序通过传送一个固定大小的消息在UDP上发送数据。如果一个程序需要重传丢失的数据包或者需要定义良好的接收顺序,需要在应用中依靠程序自己来实现这些功能。
DHCP,域名系统(DNS),网络时间协议(NTP)和虚拟可扩展局域网(VXLAN)是在OpenStack环境中使用UDP协议的典型例子。
UDP支持一对多的通信,向多台主机发送一个数据包。通过将接收端IP地址设为特殊的IP广播地址255.255.255.255,应用可以向同一网络中所有主机发送一个UDP广播包。应用也可以使用IP多播将一个UDP数据包发送到一组接收主机。接收端应用程序通过将UDP socket绑定到一个特殊的有效多播组IP地址就可以加入到一个多播组。接收端主机并不一定与发送端主机在一个本地网络中,但是中间的路由器必须配置为支持IP多播路由。VXLAN就是一个基于UDP协议使用IP多播的例子。
Internet控制报文协议是一个通过IP网络发送控制报文的协议。例如,如果一个路由器在它的路由表中找不到对应的目的地址(ICMP编码1,目的主机不可达)或者IP数据包太大以至于超出了路由器的处理能力(ICMP编码4,需要分段并且没有置位分段标识),这台路由器就会向源主机发送一个ICMP包。
ping和mtrLinux命令行工具是网络应用使用ICMP的两个例子。