# OVS原理

ovs的架构如下图所示，主要由内核datapath、vswitchd、ovsdb以及用户空间的ovs-vsctl/ovs-ofctl/ovs-dpctl等组成。

![](https://1111800802-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-L9rgYC6PUIj4mlO7_pn%2F-LDZS-yElRbis9qdjN9s%2F-LDZSDIOtTVm0n1Ehhh0%2Fovs-internal.png?generation=1527472537688500\&alt=media)

* vswitchd是一个守护进程，是ovs的管理和控制服务，通过unix socket将配置信息保存到ovsdb，并通过netlink和内核模块交互
* ovsdb则是ovs的数据库，保存了ovs配置信息
* datapath是负责数据交换的内核模块，比如把接受端口收到的包放到流表中进行匹配，并执行匹配后的动作等。它在初始化和port binding的时候注册钩子函数，把端口的报文处理接管到内核模块

## 主要数据结构

![](https://1111800802-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-L9rgYC6PUIj4mlO7_pn%2F-LDZS-yElRbis9qdjN9s%2F-LDZSDIVyBlHrLrR4LSs%2Fdata.png?generation=1527472537643382\&alt=media) (图片来自[csdn](http://blog.csdn.net/yuzhihui_no1/article/details/39188373))

## 主要流程

*注：部分转载自*[*OVS 源码分析整理*](http://www.jianshu.com/p/bf112793d658)

### 添加网桥

1. 键入命令ovs-vsctl add-br testBR
2. 内核中的 openvswitch.ko 收到一个添加网桥的命令时候——即收到 OVS\_DATAPATH\_FAMILY通道的 OVS\_DP\_CMD\_NEW命令。该命令绑定的回调函数为 ovs\_dp\_cmd\_new
3. ovs\_dp\_cmd\_new 函数除了初始化 dp 结构外，调用 new\_vport 函数来生成新的 vport
4. new\_vport 函数调用 ovs\_vport\_add()来尝试生成一个新的 vport
5. ovs\_vport\_add()函数会检查 vport 类型（通过 vport\_ops\_list\[]数组），并调用相关的 create()函数来生成 vport 结构
6. 当dp是网络设备时（vport\_netdev.c），最终由 ovs\_vport\_add()函数调用的是 netdev\_create()【在 vport\_ops\_list的ovs\_netdev\_ops 中】
7. netdev\_create()函数最关键的一步是注册了收到网包时的回调函数
8. err=netdev\_rx\_handler\_register(netdev\_vport->dev,netdev\_frame\_hook,vport);
9. 操作是将 netdev\_vport->dev 收到网包时的相关数据由 netdev\_frame\_hook()函数来处理，都是些辅助处理，依次调用各处理函数，在 netdev\_port\_receive()【这里会进行数据包的拷贝，避免损坏】进入 ovs\_vport\_receive()回到 vport.c，从 ovs\_dp\_process\_receive\_packet()回到 datapath.c，进行统一处理
10. 流程：netdev\_frame\_hook()->netdev\_port\_receive->ovs\_vport\_receive->ovs\_dp\_process\_received\_packet()
11. net\_port\_receive()首先检测是否 skb 被共享，若是则得到 packet 的拷贝。
12. net\_port\_receive()其调用ovs\_vport\_receive()，检查包的校验和，然后交付给我们的vport通用层来处理。

![](https://1111800802-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-L9rgYC6PUIj4mlO7_pn%2F-LDZS-yElRbis9qdjN9s%2F-LDZSDIaObRb5Ho6Fw06%2Fadd-bridge.jpg?generation=1527472537737414\&alt=media) (图片来自[简书](http://www.jianshu.com/p/bf112793d658))

### 流表匹配

1. flow\_lookup()查找对应的流表项
2. for 循环调用 rcu\_dereference\_ovs 对流表结构体中的 mask\_list 成员遍历，找到对应的的 成员
3. flow=masked\_flow\_lookup()遍历进行下一级 hmap查找，找到为止
4. 进入 包含函数 ovs\_flow\_mask\_key(\&masked\_key,unmasked,mask)，将最开始提取的 Key 值和 mask 的 key 值进行“与”操作，结果存放在 masked\_key 中，用来得到后面的 Hash 值
5. hash=flow\_hash(\&masked\_key,key\_start,key\_end)key 值的匹配字段只有部分
6. ovs\_vport\_add()函数会检查 vport 类型（通过 vport\_ops\_list\[]数组），并调用相关的 create()函数来生成 vport 结构
7. 可见，当 dp 时网络设备时（vport\_netdev.c），最终由 ovs\_vport\_add()函数调用的是 netdev\_create()【在 vport\_ops\_list的ovs\_netdev\_ops 中】
8. netdev\_vport->dev 收到网包时的相关数据由 netdev\_frame\_hook()函数来处理，都是些辅助处理，依次调用各处理函数，在 netdev\_port\_receive()【这里会进行数据包的拷贝，避免损坏】进入 ovs\_vport\_receive()回到 vport.c，从 ovs\_dp\_process\_receive\_packet()回到 datapath.c，进行统一处理

![](https://1111800802-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-L9rgYC6PUIj4mlO7_pn%2F-LDZS-yElRbis9qdjN9s%2F-LDZSDIgr_nEsDpuXfe-%2Fflow-lookup.jpg?generation=1527472536626393\&alt=media)

### 收包处理

1. ovs\_vport\_receive\_packets()调用ovs\_flow\_extract基于skb生成key值，并检查是否有错,然后调用ovs\_dp\_process\_packet。交付给datapath处理
2. ovs\_flow\_tbl\_lookup\_stats。基于前面生成的key值进行流表查找，返回匹配的流表项，结构为sw\_flow。&#x20;
3. 若不存在匹配，则调用ovs\_dp\_upcall上传至userspace进行匹配。 (包括包和key都要上传)&#x20;
4. 若存在匹配，则直接调用ovs\_execute\_actions执行对应的action，比如添加vlan头，转发到某个port等。

![](https://1111800802-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-L9rgYC6PUIj4mlO7_pn%2F-LDZS-yElRbis9qdjN9s%2F-LDZSDIjoEZRk9Ju-SFU%2Freceive-packet.jpg?generation=1527472536489496\&alt=media)

### upcall 消息处理

1. ovs\_dp\_upcall()首先调用 err=queue\_userspace\_packet()将信息排队发到用户空间去
2. dp\_ifindex=get\_dpifindex(dp)获取网卡设备索引号
3. 调整 VLAN的 MAC 地址头指针
4. 网络链路属性，如果不需要填充则调用此函数
5. len=upcall\_msg\_size()，获得 upcall 发送消息的大小
6. user\_skb=genlmsg\_new\_unicast，创建一个新的 netlink 消息
7. upcall=genlmsg\_put()增加一个新的 netlink 消息到 skb
8. err=genlmsg\_unicast(),发送消息到用户空间去处理

![](https://1111800802-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-L9rgYC6PUIj4mlO7_pn%2F-LDZS-yElRbis9qdjN9s%2F-LDZSDIlKJx05ewhgc_e%2Fupcall.jpg?generation=1527472516639083\&alt=media)

## 参考文档

* [OVS 源码分析整理](http://www.jianshu.com/p/bf112793d658)
* [openvswitch源码分析](http://blog.csdn.net/column/details/openvswitch.html)
* [OVS Deep Dive](http://docs.openvswitch.org/en/latest/topics/)
