vxlan

转自:http://www.tuicool.com/articles/6zMJRn

一、深入理解 VXLAN

VXLAN 是非常新的一个 tunnel 技术,它是一个 L2 tunnel。Linux 内核的 upstream 中也刚刚加入 VXLAN 的实现。相比 GRE tunnel 它有着很的扩展性,同时解决了很多其它问题。

一,GRE tunnel 的不足

网络很多介绍 VXLAN 的文章都没有直接告诉你相比较 GRE tunnel,VXLAN 的优势在哪里,或者说 GRE tunnel 的不足在哪里。为了更好的了解 VXLAN,我们有必要看一下 GRE tunnel 的不足。

在我前面写的介绍 GRE tunnel 的文章中,其实并不容易看出 GRE tunnel 的不足之处。根本原因是图中给出的例子不太好,只有两个网络的话 GRE tunnel 的不足凸显不出来,让我们看看有三个网络的情况如何用 GRE tunnel 互联,如下图所示:


这下子就很明显了,要让这三个网络互联,我们需要建立三个 GRE tunnel。如果网络数量再增长,那么需要的 tunnel 数量更多。换句话说,GRE tunnel 的扩展性太差,从根本上讲还是因为它只是一个 point to point 的 tunnel。

二,VLAN 的不足

其实 VLAN 在某种程度上也可以看作一个 L2 over L2 的 tunnel,只不过它多了一个新的 VLAN header,这其中有12 bit 是 VLAN tag。所以 VLAN 的第一个不足之处就是它最多只支持 4096 个 VLAN 网络(当然这还要除去几个预留的),对于大型数据中心的来说,这个数量是远远不够的。

第二个不足就是,VLAN 这个所谓的 tunnel 是基于 L2 的,所以很难跨越 L2 的边界,在很大程度上限制了网络的灵活性。同时,VLAN 操作需手工介入较多,这对于管理成千上万台机器的管理员来说是难以接受的。

三,VXLAN 的引入

VXLAN 是 Virtual eXtensible LANs 的缩写,所以顾名思义,它是对 VLAN 的一个扩展,但又不仅限于此。

从数量上讲,它确实把 12 bit 的 VLAN tag 扩展成了 24 bit,所以至少暂时够用的了。从实现上讲,它是 L2 over UDP,它利用了 UDP 同时也是 IPv4 的单播和多播,可以跨 L3 边界,很巧妙地解决了 GRE tunnel 和 VLAN 存在的不足,让组网变得更加灵活。

四,VXLAN 的实现

VXLAN 的配置可以参考内核文档 Documentation/networking/vxlan.txt,本人目前还没有环境测试,所以只能做一些代码分析了。

Linux 内核中对 VXLAN 的实现是在 drivers/net/vxlan.c 源文件中,是由 Stephen Hemminger (iproute2 的维护者)完成的。代码质量相当高,所以可读性也很好,强烈推荐阅读一下。

看代码之前先看 VXLAN 的头是一个怎样的结构,如下图所示(图片来自参考资料4):



好了,现在我们可以看代码了。先看发送端,vxlan_xmit() 函数。首先需要说的是发送之前内核会检查目的地址,如果是L2 multicast,那么应该发送到 VXLAN group 组播地址,否则,如果 MAC 地址是已知的,直接单播到对应的 IP;如果未知,则广播到组播地址。代码如下,比文档还要好读。:-)

C:
  1. static __be32 vxlan_find_dst ( struct vxlan_dev * vxlan, struct sk_buff * skb )
  2. {
  3.         const struct ethhdr * eth = ( struct ethhdr * ) skb - >data;
  4.         const struct vxlan_fdb * f;
  5.  
  6.         if ( is_multicast_ether_addr ( eth - >h_dest ) )
  7.                 return vxlan - >gaddr;
  8.  
  9.         f = vxlan_find_mac ( vxlan, eth - >h_dest ) ;
  10.         if ( f )
  11.                 return f - >remote_ip;
  12.         else
  13.                 return vxlan - >gaddr;
  14.  
  15. }

剩下的基本上就是一层一层的往外添加头了,依次添加 VXLAN header,UDP header,IP header:

C:
  1. //...
  2.         vxh = ( struct vxlanhdr * ) __skb_push ( skb, sizeof ( * vxh ) ) ;
  3.         vxh - >vx_flags = htonl ( VXLAN_FLAGS ) ;
  4.         vxh - >vx_vni = htonl ( vxlan - >vni << 8 ) ;
  5.  
  6.         __skb_push ( skb, sizeof ( * uh ) ) ;
  7.         skb_reset_transport_header ( skb ) ;
  8.         uh = udp_hdr ( skb ) ;
  9.  
  10.         uh - >dest = htons ( vxlan_port ) ;
  11.         uh - >source = htons ( src_port ) ;
  12.  
  13.         uh - >len = htons ( skb - >len ) ;
  14.         uh - >check = 0 ;
  15.  
  16.         __skb_push ( skb, sizeof ( * iph ) ) ;
  17.         skb_reset_network_header ( skb ) ;
  18.         iph             = ip_hdr ( skb ) ;
  19.         iph - >version     = 4 ;
  20.         iph - >ihl         = sizeof ( struct iphdr ) >> 2 ;
  21.         iph - >frag_off   = df;
  22.         iph - >protocol   = IPPROTO_UDP;
  23.         iph - >tos         = vxlan_ecn_encap ( tos, old_iph, skb ) ;
  24.         iph - >daddr       = dst;
  25.         iph - >saddr       = fl4. saddr ;
  26.         iph - >ttl         = ttl ? : ip4_dst_hoplimit ( & rt - >dst ) ;
  27.  
  28.         vxlan_set_owner ( dev, skb ) ;

正如 GRE tunnel,比较复杂的地方是在接收端。因为 VXLAN 利用了 UDP,所以它在接收的时候势必需要有一个 UDP server 在监听某个端口,这个是在 VXLAN 初始化的时候完成的,即 vxlan_init_net() 函数:

C:
  1. static __net_init int vxlan_init_net ( struct net * net )
  2. {
  3.     struct vxlan_net * vn = net_generic ( net, vxlan_net_id ) ;
  4.     struct sock * sk;
  5.     struct sockaddr_in vxlan_addr = {
  6.         . sin_family = AF_INET,
  7.         . sin_addr . s_addr = htonl ( INADDR_ANY ) ,
  8.     } ;
  9.     int rc;
  10.     unsigned h;
  11.  
  12.     /* Create UDP socket for encapsulation receive. */
  13.     rc = sock_create_kern ( AF_INET, SOCK_DGRAM, IPPROTO_UDP, & vn - >sock ) ;
  14.     if ( rc < 0 ) {
  15.         pr_debug ( "UDP socket create failed \n " ) ;
  16.         return rc;
  17.     }
  18.     /* Put in proper namespace */
  19.     sk = vn - >sock - >sk;
  20.     sk_change_net ( sk, net ) ;
  21.  
  22.     vxlan_addr. sin_port = htons ( vxlan_port ) ;
  23.  
  24.     rc = kernel_bind ( vn - >sock, ( struct sockaddr * ) & vxlan_addr,
  25.               sizeof ( vxlan_addr ) ) ;
  26.     if ( rc < 0 ) {
  27.         pr_debug ( "bind for UDP socket %pI4:%u (%d) \n ",
  28.               & vxlan_addr. sin_addr , ntohs ( vxlan_addr. sin_port ) , rc ) ;
  29.         sk_release_kernel ( sk ) ;
  30.         vn - >sock = NULL ;
  31.         return rc;
  32.     }
  33.  
  34.     /* Disable multicast loopback */
  35.     inet_sk ( sk ) - >mc_loop = 0 ;
  36.  
  37.     /* Mark socket as an encapsulation socket. */
  38.     udp_sk ( sk ) - >encap_type = 1 ;
  39.     udp_sk ( sk ) - >encap_rcv = vxlan_udp_encap_recv;
  40.     udp_encap_enable ( ) ;
  41.  
  42.     for ( h = 0 ; h <VNI_HASH_SIZE; ++ h )
  43.         INIT_HLIST_HEAD ( & vn - >vni_list [ h ] ) ;
  44.  
  45.     return 0 ;
  46. }

由此可见内核内部创建 socket 的 API 是sock_create_kern(),bind() 对应的是 kernel_bind()。注意到这里实现了一个hook,vxlan_udp_encap_recv(),这个正是接收端的主要代码。

发送端是一层一层往外填,那么接收端一定就是一层一层外里剥:

C:
  1. /* pop off outer UDP header */
  2.     __skb_pull ( skb, sizeof ( struct udphdr ) ) ;
  3.  
  4.     /* Need Vxlan and inner Ethernet header to be present */
  5.     if ( ! pskb_may_pull ( skb, sizeof ( struct vxlanhdr ) ) )
  6.         goto error;
  7.  
  8.     /* Drop packets with reserved bits set */
  9.     vxh = ( struct vxlanhdr * ) skb - >data;
  10.     if ( vxh - >vx_flags != htonl ( VXLAN_FLAGS ) ||
  11.         ( vxh - >vx_vni & htonl ( 0xff ) ) ) {
  12.         netdev_dbg ( skb - >dev, "invalid vxlan flags=%#x vni=%#x \n " ,
  13.                ntohl ( vxh - >vx_flags ) , ntohl ( vxh - >vx_vni ) ) ;
  14.         goto error;
  15.     }
  16.  
  17.     __skb_pull ( skb, sizeof ( struct vxlanhdr ) ) ;
  18.  
  19.     /* Is this VNI defined? */
  20.     vni = ntohl ( vxh - >vx_vni ) >> 8 ;
  21.     vxlan = vxlan_find_vni ( sock_net ( sk ) , vni ) ;
  22.     if ( ! vxlan ) {
  23.         netdev_dbg ( skb - >dev, "unknown vni %d \n " , vni ) ;
  24.         goto drop;
  25.     }
  26.  
  27.     if ( ! pskb_may_pull ( skb, ETH_HLEN ) ) {
  28.         vxlan - >dev - >stats. rx_length_errors ++ ;
  29.         vxlan - >dev - >stats. rx_errors ++ ;
  30.         goto drop;
  31.     }

在重新入栈之前还要做一些准备工作:

C:
  1. /* Re-examine inner Ethernet packet */
  2.     oip = ip_hdr ( skb ) ;
  3.     skb - >protocol = eth_type_trans ( skb, vxlan - >dev ) ;
  4.  
  5.     /* Ignore packet loops (and multicast echo) */
  6.     if ( compare_ether_addr ( eth_hdr ( skb ) - >h_source,
  7.                    vxlan - >dev - >dev_addr ) == 0 )
  8.         goto drop;
  9.  
  10.     if ( vxlan - >learn )
  11.         vxlan_snoop ( skb - >dev, oip - >saddr, eth_hdr ( skb ) - >h_source ) ;
  12.  
  13.     __skb_tunnel_rx ( skb, vxlan - >dev ) ;
  14.     skb_reset_network_header ( skb ) ;
  15.     skb - >ip_summed = CHECKSUM_NONE;

另外需要特别指出的是:1) 加入和离开组播地址是在 vxlan_open() 和 vxlan_stop() 中完成的;2) Linux 内核已经把 bridge 的 L2 learn 功能给抽出来了,所以 VXLAN 也实现了对 L2 地址的学习和转发:

C:
  1. static const struct net_device_ops vxlan_netdev_ops = {
  2.     . ndo_init       = vxlan_init,
  3.     . ndo_open       = vxlan_open,
  4.     . ndo_stop       = vxlan_stop,
  5.     . ndo_start_xmit     = vxlan_xmit,
  6.     . ndo_get_stats64     = vxlan_stats64,
  7.     . ndo_set_rx_mode     = vxlan_set_multicast_list,
  8.     . ndo_change_mtu     = eth_change_mtu,
  9.     . ndo_validate_addr   = eth_validate_addr,
  10.     . ndo_set_mac_address     = eth_mac_addr,
  11.     . ndo_fdb_add         = vxlan_fdb_add,
  12.     . ndo_fdb_del         = vxlan_fdb_delete,
  13.     . ndo_fdb_dump       = vxlan_fdb_dump,
  14. } ;

附注:openvswitch 中的 VXLAN 的实现: http://openvswitch.org/pipermail/dev/2011-October/012051.html

参考资料:

1. http://tools.ietf.org/html/draft-mahalingam-dutt-dcops-vxlan-02
2. http://blogs.cisco.com/datacenter/digging-deeper-into-vxlan/
3. http://www.yellow-bricks.com/2012/11/02/vxlan-use-cases/
4. http://www.borgcube.com/blogs/2011/11/vxlan-primer-part-1/
5. http://www.borgcube.com/blogs/2012/03/vxlan-primer-part-2-lets-get-physical/
6. http://it20.info/2012/05/typical-vxlan-use-case/




二、vxlan隧道技术介绍——转自:http://blog.csdn.net/freezgw1985/article/details/16354897

一 . 为什么需要Vxlan

1. vlan的数量限制
   4096个vlan远不能满足大规模云计算数据中心的需求

2. 物理网络基础设施的限制
   基于I P子网的区域划分限制了需要 二层网络连通性的应用负载的 部署

3. TOR交换机MAC表耗尽
    虚拟化以及东西向流量导致更多的MAC表项

4. 多租户场景
    IP地址重叠?
二. 什么是Vxlan

1. Vxlan报文
    vxlan(virtual Extensible LAN)虚拟可扩展局域网,是一种overlay的网络技术,使用MAC in UDP的方法进
行封装,共50字节的封装报文头。具体的报文格式如下:


(1) vxlan header
    共计8个字节,目前使用的是Flags中的一个8bit的标识位和24bit的VNI(Vxlan Network identifier),
其余部分没有定义,但是在使用的时候必须设置为0x0000。

(2) 外层的UDP报头
     目的端口使用4798,但是可以根据需要进行修改。同事UDP的校验和必须设置成全0。

(3) IP报文头
     目的IP地址可以是单播地址,也可以是多播地址。单播情况下,目的IP地址是Vxlan Tunnel End Point
(VTEP)的IP地址。在多播情况下引入VXLAN管理层, 利用VNI和IP多播组的映射来确定VTEPs。???
  • protocol:设置值为0x11,显示说明这是UDP数据包
  • Source ip: 源vTEP_IP;
  • Destination ip: 目的VTEP IP。
(4) Ethernet Header
  • Destination Address:目的VTEP的Mac 地址,即为本地下一跳的地址(通常是网关Mac 地址);
  • VLAN: VLAN Type被设置为0x8100, 并可以设置Vlan Id tag(这就是vxlan的vlan 标签)。
  • Ethertype:设置值为0x8000,指明数据包为IPv4的。
补充:VTEP的作用?    
     用于对VXLAN报文进行封装/解封装,包括ARP请求报文和正常的VXLAN数据报文,在一段封装报文
后通过隧道向另一端VTEP发送封装报文,另一端VTEP接收到封装的报文解封装后根据封装的MAC地址
进行装法。VTEP可由支持VXLAN的硬件设备或软件来实现。

   从封装的结构上来看,VXLAN提供了将二层网络overlay在三层网络上的能力,VXLAN Header中的VNI有
24个bit,数量远远大于4096,并且UDP的封装可以穿越三层网络,比VLAN有更好的扩展性。

2. Vxlan的数据和控制平面
  (1) 数据平面---隧道机制
     已经知道,VTEP为虚拟机的数据包加上了层包头,这些新的报头之有在数据到达目的VTEP后才会被去掉。
中间路径的网络设备只会根据外层包头内的目的地址进行数据转发,对于转发路径上的网络来说,一个Vxlan
数据包跟一个普通IP包相比,出了个头大一点外没有区别。
     由于VXLAN的数据包在整个转发过程中保持了内部数据的完整,因此VXLAN的数据平面是一个基于隧道
的数据平面。

(2) 控制平面----改进的二层协议
     VXLAN不会在虚拟机之间维持一个长连接,所以VXLAN需要一个控制平面来记录对端地址可达情况。控制
平面的表为(VNI,内层MAC,外层vtep_ip)。Vxlan学习地址的时候仍然保存着二层协议的特征,节点之间不会
周期性的交换各自的路由表,对于不认识的MAC地址,VXLAN依靠 组播来获取路径信息(如果有SDN Controller,
可以向SDN单播获取)。
    另一方面,VXLAN还有自学习的功能,当VTEP收到一个UDP数据报后,会检查自己是否收到过这个虚拟机的
数据,如果没有,VTEP就会记录源vni/源外层ip/源内层mac对应关系,避免组播学习。


3. VxlanARP请求

(1) vxlan初始化



 VM1和VM2连接到VXLAN网络(VNI)100,两个VXLAN主机加入IP多播组239.119.1.1

(2) ARP请求


   1) VM1以广播的形式发送ARP请求;
   2) VTEP1封装报文。打上VXLAN标识为100,外层IP头DA为IP多播组(239.119.1.1),SA为IP_VTEP1.
   3) VTEP1在多播组内进行多播;
   4) VTEP2解析接收到多播报文。填写流表(VNI, 内层mac地址,外层Ip地址),并在本地VXLAN标识为100的范围内
       广播(是VXLAN的用武之地)。
   5) VM2对接收到的ARP请求进行响应;

(3) ARP应答

  1) VM2准备ARP响应报文后向VM1发送响应报文

       2)VTEP2接收到VM2的响应报文后把它封装在ip单播报文中(VXLAN标识依然为100),然 后向VM1发送单播

       3)VTEP1接收到单播报文后,学习内层MAC到外层ip地址的映射,解封装并根据被封装内容的目的MAC地址转发给VM1

       4)VM1接收到ARP应答报文,ARP交互结束

4  数据传输
    (1)  ARP请求应答之后,VM1知道了VM2的Mac地址,并且要向VM2通信(注意,VM1是以TCP的方法向VM2发送数据的)。
VTEP1 收到VM1发送数据包,用MAC地址从流表中检查VM1与VM2是否属于用一个VNI。两个VM不但位于同一个VNI中
(不在同一个VNI中出网关),并且VTEP1已经知道了VM2的所有地址信息(MAC和VTEP2_IP)。VTEP1封装新的数据包。然后
交给上联交换机。
   (2) 上联交换机收到服务器发来的UDP包,对比目的IP地址和自己的路由表,然后将数据报转发给相应的端口。
   (3) 目的VTEP收到数据包后检查器VNI,如果UDP报中VNI与VM2的VNI一致,则将数据包解封装后交给VM2进一步处理。至此
一个数据包传输完成。整个Vxlan相关的行为(可能穿越多个网关)对虚拟机来说是透明的,虚拟机不会感受传输的过程。

    虽然VM1与VM2之间启动了TCP来传输数据,但数据包一路上实际是以UDP的形式被转发,两端的VTEP并不会检查数据是否
正确或者顺序是否完整,所有的这些工作都是在VM1和VM2在接收到解封装的TCP包后完成的。也就是说如果说如果被UDP封装
的是TCP连接,那么UDP和TCP将做为两个独立的协议栈各自工作,相互之间没有交互。
    
5 Vxlan网关


 如果需要VXLAN网络和非VXLAN网络连接,必须使用VXLAN网关才能把VXLAN网络和外部网络进行桥接和
完成VXLAN ID和VLAN ID之间的映射和路由,和VLAN一样,VXLAN网络之间的通信也需要三层设备的支持,
即VXLAN路由的支持。同样VXLAN网关可由硬件和软件来实现。

 从封装的结构上来看,VXLAN提供了将二层网络overlay在三层网络上的能力,VXLAN Header中的VNI有
24个bit,数量远远大于4096,并且UDP的封装可以穿越三层网络,比VLAN有更好的扩展性。

6.部署
(1) 纯VXLAN部署场景
  对于连接到VXLAN内的虚拟机,由于虚拟机的VLAN信息不再作为转发的依据,虚拟机的迁移也就
不再受三层网关的限制,可以实现跨越三层网关的迁移。


(2) VXLAN与VLAN混合部署

   为了实现VLAN和VXLAN之间互通,VXLAN定义了VXLAN网关。VXLAN网关上同时存在两种类型的端口:VXLAN端口
和普通端口。
   当收到从VXLAN网络到普通网络的数据时,VXLAN网关去掉外层包头,根据内层的原始帧头转发到普通端口上;当有数据
从普通网络进入到VXLAN网络时,VXLAN网关负责打上外层包头,并根据原始VLAN ID对应到一个VNI,同时去掉内层包头
的VLAN ID信息。相应的如果VXLAN网关发现一个VXLAN包的内层帧头上还带有原始的二层VLAN ID,会直接将这个包丢弃。
之所以这样,是VLAN ID是一个本地信息,仅仅在一个地方的二层网络上其作用,VXLAN是隧道机制,并不依赖VLAN ID进行
转发,也无法检查VLAN ID正确与否。因此,VXLAN网关连接传统网络的端口必须配置ACCESS口,不能启用TRUNK口。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值